diff --git a/README.md b/README.md index dc4200c1..3b8225f2 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,23 @@ libp2p-swarm is used by libp2p but it can be also used as a standalone module. libp2p-swarm is available on npm and so, like any other npm module, just: ```bash -$ npm install libp2p-swarm --save +> npm install libp2p-swarm --save ``` And use it in your Node.js code as: ```JavaScript -var Swarm = require('libp2p-swarm') +const Swarm = require('libp2p-swarm') -var sw = new Swarm(peerInfoSelf) +const sw = new Swarm(peerInfo) ``` -peerInfoSelf is a [PeerInfo](https://github.com/diasdavid/js-peer-info) object that represents the peer creating this swarm instance. +peerInfo is a [PeerInfo](https://github.com/diasdavid/js-peer-info) object that represents the peer creating this swarm instance. + + +---------- +BELOW NEEDS AN UPDATE + ### Support a transport diff --git a/package.json b/package.json index 180bb5bc..f6987119 100644 --- a/package.json +++ b/package.json @@ -26,28 +26,26 @@ "test" ], "engines": { - "node": "^4.0.0" + "node": "^4.3.0" }, "devDependencies": { + "bl": "^1.1.2", "chai": "^3.5.0", "istanbul": "^0.4.2", "libp2p-spdy": "^0.1.0", - "libp2p-tcp": "^0.1.1", + "libp2p-tcp": "^0.2.1", "mocha": "^2.4.5", + "multiaddr": "^1.1.1", + "peer-id": "^0.5.1", + "peer-info": "^0.5.2", "pre-commit": "^1.1.2", - "sinon": "^1.15.4", "standard": "^6.0.7", "stream-pair": "^1.0.3" }, "dependencies": { "async": "^1.3.0", "ip-address": "^5.0.2", - "ipfs-logger": "^0.1.0", - "multiaddr": "^1.0.0", - "multiplex-stream-muxer": "^0.2.0", "multistream-select": "^0.6.1", - "peer-id": "^0.3.3", - "peer-info": "^0.3.2", "protocol-buffers-stream": "^1.2.0" } } diff --git a/src/index.js b/src/index.js index 54c25426..f933418c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,14 +1,161 @@ -var multistream = require('multistream-select') -var async = require('async') -var identify = require('./identify') +// const multistream = require('multistream-select') +// const async = require('async') +// const identify = require('./identify') +const PassThrough = require('stream').PassThrough exports = module.exports = Swarm function Swarm (peerInfo) { - var self = this + if (!(this instanceof Swarm)) { + return new Swarm(peerInfo) + } - if (!(self instanceof Swarm)) { - throw new Error('Swarm must be called with new') + if (!peerInfo) { + throw new Error('You must provide a value for `peerInfo`') + } + + // transports -- + + // { key: transport }; e.g { tcp: } + this.transports = {} + + this.transport = {} + + this.transport.add = (key, transport, options, callback) => { + if (typeof options === 'function') { + callback = options + options = {} + } + if (!callback) { callback = noop } + + if (this.transports[key]) { + throw new Error('There is already a transport with this key') + } + this.transports[key] = transport + callback() + } + + this.transport.dial = (key, multiaddrs, callback) => { + const t = this.transports[key] + + // a) if multiaddrs.length = 1, return the conn from the + // transport, otherwise, create a passthrough + if (!Array.isArray(multiaddrs)) { + multiaddrs = [multiaddrs] + } + if (multiaddrs.length === 1) { + const conn = t.dial(multiaddrs.shift(), {ready: () => { + const cb = callback + callback = noop // this is done to avoid connection drops as connect errors + cb(null, conn) + }}) + conn.once('error', () => { + callback(new Error('failed to connect to every multiaddr')) + }) + return conn + } + + // b) multiaddrs should already be a filtered list + // specific for the transport we are using + const pt = new PassThrough() + + next(multiaddrs.shift()) + return pt + function next (multiaddr) { + const conn = t.dial(multiaddr, {ready: () => { + pt.pipe(conn).pipe(pt) + const cb = callback + callback = noop // this is done to avoid connection drops as connect errors + cb(null, pt) + }}) + + conn.once('error', () => { + if (multiaddrs.length === 0) { + return callback(new Error('failed to connect to every multiaddr')) + } + next(multiaddrs.shift()) + }) + } + } + + this.transport.listen = (key, options, handler, callback) => { + // if no callback is passed, we pass conns to connHandler + if (!handler) { handler = connHandler } + + const multiaddrs = peerInfo.multiaddrs.filter((m) => { + if (m.toString().indexOf('tcp') !== -1) { + return m + } + }) + + this.transports[key].createListener(multiaddrs, handler, (err, maUpdate) => { + if (err) { + return callback(err) + } + if (maUpdate) { + // because we can listen on port 0... + peerInfo.multiaddr.replace(multiaddrs, maUpdate) + } + + callback() + }) + } + + this.transport.close = (key, callback) => { + this.transports[key].close(callback) + } + + // connections -- + + // { peerIdB58: { conn: }} + this.conns = {} + + // { + // peerIdB58: { + // muxerName: { + // muxer: + // rawSocket: socket // to abstract info required for the Identify Protocol + // } + // } + this.muxedConns = {} + + this.connection = {} + this.connection.addUpgrade = () => {} + this.connection.addStreamMuxer = () => {} + + // enable the Identify protocol + this.connection.reuse = () => {} + + // main API - higher level abstractions -- + + this.dial = () => { + // TODO + } + this.handle = (protocol, callback) => { + // TODO + } + this.close = (callback) => { + var count = 0 + + Object.keys(this.transports).forEach((key) => { + this.transports[key].close(() => { + if (++count === Object.keys(this.transports).length) { + callback() + } + }) + }) + } + + function connHandler (conn) { + // do all the multistream select handshakes (this should be probably done recursively + } +} + +/* +function Swarm (peerInfo) { + var self = this + if (!(this instanceof Swarm)) { + return new Swarm(peerInfo) } if (!peerInfo) { @@ -341,3 +488,6 @@ function Swarm (peerInfo) { }) } } +*/ + +function noop () {} diff --git a/tests/swarm-old.js b/tests/swarm-old.js new file mode 100644 index 00000000..0acd7654 --- /dev/null +++ b/tests/swarm-old.js @@ -0,0 +1,354 @@ +/* eslint-env mocha */ + +var async = require('async') +var expect = require('chai').expect + +var multiaddr = require('multiaddr') +var Id = require('peer-id') +var Peer = require('peer-info') +var Swarm = require('../src') +var tcp = require('libp2p-tcp') +var Spdy = require('libp2p-spdy') + +// because of Travis-CI +process.on('uncaughtException', function (err) { + console.log('Caught exception: ' + err) +}) + +describe('Basics', function () { + it('enforces creation with new', function (done) { + expect(function () { + Swarm() + }).to.throw() + done() + }) + + it('it throws an exception without peerSelf', function (done) { + expect(function () { + var sw = new Swarm() + sw.close() + }).to.throw(Error) + done() + }) +}) + +describe('When dialing', function () { + describe('if the swarm does add any of the peer transports', function () { + it('it returns an error', function (done) { + var peerOne = new Peer(Id.create(), [multiaddr('/ip4/127.0.0.1/tcp/8090')]) + var peerTwo = new Peer(Id.create(), [multiaddr('/ip4/127.0.0.1/tcp/8091')]) + var swarm = new Swarm(peerOne) + + swarm.dial(peerTwo, {}, function (err) { + expect(err).to.exist + done() + }) + }) + }) +}) + +describe('Without a Stream Muxer', function () { + describe('and one swarm over tcp', function () { + it('add the transport', function (done) { + var mh = multiaddr('/ip4/127.0.0.1/tcp/8010') + var p = new Peer(Id.create(), []) + var sw = new Swarm(p) + + sw.addTransport('tcp', tcp, { multiaddr: mh }, {}, {port: 8010}, ready) + + function ready () { + expect(sw.transports['tcp'].options).to.deep.equal({}) + expect(sw.transports['tcp'].dialOptions).to.deep.equal({}) + expect(sw.transports['tcp'].listenOptions).to.deep.equal({port: 8010}) + expect(sw.transports['tcp'].transport).to.deep.equal(tcp) + + sw.close(done) + } + }) + }) + + describe('and two swarms over tcp', function () { + var mh1, p1, sw1, mh2, p2, sw2 + + beforeEach(function (done) { + mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010') + p1 = new Peer(Id.create(), []) + sw1 = new Swarm(p1) + + mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020') + p2 = new Peer(Id.create(), []) + sw2 = new Swarm(p2) + + async.parallel([ + function (cb) { + sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, cb) + }, + function (cb) { + sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, cb) + } + ], done) + }) + + afterEach(function (done) { + async.parallel([sw1.close, sw2.close], done) + }) + + it('dial a conn', function (done) { + sw1.dial(p2, {}, function (err) { + expect(err).to.equal(undefined) + expect(Object.keys(sw1.conns).length).to.equal(1) + done() + }) + }) + + it('dial a conn on a protocol', function (done) { + sw2.handleProtocol('/sparkles/1.0.0', function (conn) { + conn.end() + conn.on('end', done) + }) + + sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { + expect(err).to.equal(null) + expect(Object.keys(sw1.conns).length).to.equal(0) + conn.end() + }) + }) + + it('dial a protocol on a previous created conn', function (done) { + sw2.handleProtocol('/sparkles/1.0.0', function (conn) { + conn.end() + conn.on('end', done) + }) + + sw1.dial(p2, {}, function (err) { + expect(err).to.equal(undefined) + expect(Object.keys(sw1.conns).length).to.equal(1) + + sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { + expect(err).to.equal(null) + expect(Object.keys(sw1.conns).length).to.equal(0) + + conn.end() + }) + }) + }) + + // it('add an upgrade', function (done) { done() }) + // it('dial a conn on top of a upgrade', function (done) { done() }) + // it('dial a conn on a protocol on top of a upgrade', function (done) { done() }) + }) + + /* TODO + describe('udp', function () { + it('add the transport', function (done) { done() }) + it('dial a conn', function (done) { done() }) + it('dial a conn on a protocol', function (done) { done() }) + it('add an upgrade', function (done) { done() }) + it('dial a conn on top of a upgrade', function (done) { done() }) + it('dial a conn on a protocol on top of a upgrade', function (done) { done() }) + }) */ + + /* TODO + describe('udt', function () { + it('add the transport', function (done) { done() }) + it('dial a conn', function (done) { done() }) + it('dial a conn on a protocol', function (done) { done() }) + it('add an upgrade', function (done) { done() }) + it('dial a conn on top of a upgrade', function (done) { done() }) + it('dial a conn on a protocol on top of a upgrade', function (done) { done() }) + }) */ + +/* TODO +describe('utp', function () { + it('add the transport', function (done) { done() }) + it('dial a conn', function (done) { done() }) + it('dial a conn on a protocol', function (done) { done() }) + it('add an upgrade', function (done) { done() }) + it('dial a conn on top of a upgrade', function (done) { done() }) + it('dial a conn on a protocol on top of a upgrade', function (done) { done() }) +}) */ +}) + +describe('With a SPDY Stream Muxer', function () { + describe('and one swarm over tcp', function () { + // TODO: What is the it here? + it('add Stream Muxer', function (done) { + // var mh = multiaddr('/ip4/127.0.0.1/tcp/8010') + var p = new Peer(Id.create(), []) + var sw = new Swarm(p) + sw.addStreamMuxer('spdy', Spdy, {}) + + done() + }) + }) + + describe('and two swarms over tcp', function () { + var mh1, p1, sw1, mh2, p2, sw2 + + beforeEach(function (done) { + mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010') + p1 = new Peer(Id.create(), []) + sw1 = new Swarm(p1) + sw1.addStreamMuxer('spdy', Spdy, {}) + + mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020') + p2 = new Peer(Id.create(), []) + sw2 = new Swarm(p2) + sw2.addStreamMuxer('spdy', Spdy, {}) + + async.parallel([ + function (cb) { + sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, cb) + }, + function (cb) { + sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, cb) + } + ], done) + }) + + function afterEach (done) { + var cleaningCounter = 0 + sw1.closeConns(cleaningUp) + sw2.closeConns(cleaningUp) + + sw1.closeListener('tcp', cleaningUp) + sw2.closeListener('tcp', cleaningUp) + + function cleaningUp () { + cleaningCounter++ + // TODO FIX: here should be 4, but because super wrapping of + // streams, it makes it so hard to properly close the muxed + // streams - https://github.com/indutny/spdy-transport/issues/14 + if (cleaningCounter < 3) { + return + } + + done() + } + } + + it('dial a conn on a protocol', function (done) { + sw2.handleProtocol('/sparkles/1.0.0', function (conn) { + // formallity so that the conn starts flowing + conn.on('data', function (chunk) {}) + + conn.end() + conn.on('end', function () { + expect(Object.keys(sw1.muxedConns).length).to.equal(1) + expect(Object.keys(sw2.muxedConns).length).to.equal(0) + afterEach(done) + }) + }) + + sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { + conn.on('data', function () {}) + expect(err).to.equal(null) + expect(Object.keys(sw1.conns).length).to.equal(0) + conn.end() + }) + }) + + it('dial two conns (transport reuse)', function (done) { + sw2.handleProtocol('/sparkles/1.0.0', function (conn) { + // formality so that the conn starts flowing + conn.on('data', function (chunk) {}) + + conn.end() + conn.on('end', function () { + expect(Object.keys(sw1.muxedConns).length).to.equal(1) + expect(Object.keys(sw2.muxedConns).length).to.equal(0) + + afterEach(done) + }) + }) + + sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { + // TODO Improve clarity + sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { + conn.on('data', function () {}) + expect(err).to.equal(null) + expect(Object.keys(sw1.conns).length).to.equal(0) + + conn.end() + }) + + conn.on('data', function () {}) + expect(err).to.equal(null) + expect(Object.keys(sw1.conns).length).to.equal(0) + + conn.end() + }) + }) + }) + + describe('and two identity enabled swarms over tcp', function () { + var mh1, p1, sw1, mh2, p2, sw2 + + beforeEach(function (done) { + mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010') + p1 = new Peer(Id.create(), []) + sw1 = new Swarm(p1) + sw1.addStreamMuxer('spdy', Spdy, {}) + sw1.enableIdentify() + + mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020') + p2 = new Peer(Id.create(), []) + sw2 = new Swarm(p2) + sw2.addStreamMuxer('spdy', Spdy, {}) + sw2.enableIdentify() + + async.parallel([ + function (cb) { + sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, cb) + }, + function (cb) { + sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, cb) + } + ], done) + }) + + afterEach(function (done) { + var cleaningCounter = 0 + sw1.closeConns(cleaningUp) + sw2.closeConns(cleaningUp) + + sw1.closeListener('tcp', cleaningUp) + sw2.closeListener('tcp', cleaningUp) + + function cleaningUp () { + cleaningCounter++ + // TODO FIX: here should be 4, but because super wrapping of + // streams, it makes it so hard to properly close the muxed + // streams - https://github.com/indutny/spdy-transport/issues/14 + if (cleaningCounter < 3) { + return + } + // give time for identify to finish + setTimeout(function () { + expect(Object.keys(sw2.muxedConns).length).to.equal(1) + done() + }, 500) + } + }) + + it('identify', function (done) { + sw2.handleProtocol('/sparkles/1.0.0', function (conn) { + // formallity so that the conn starts flowing + conn.on('data', function (chunk) {}) + + conn.end() + conn.on('end', function () { + expect(Object.keys(sw1.muxedConns).length).to.equal(1) + done() + }) + }) + + sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { + conn.on('data', function () {}) + expect(err).to.equal(null) + expect(Object.keys(sw1.conns).length).to.equal(0) + conn.end() + }) + }) + }) +}) diff --git a/tests/swarm-test.js b/tests/swarm-test.js index 0acd7654..a9ecfb26 100644 --- a/tests/swarm-test.js +++ b/tests/swarm-test.js @@ -1,354 +1,246 @@ /* eslint-env mocha */ -var async = require('async') -var expect = require('chai').expect +const expect = require('chai').expect +// const async = require('async') -var multiaddr = require('multiaddr') -var Id = require('peer-id') -var Peer = require('peer-info') -var Swarm = require('../src') -var tcp = require('libp2p-tcp') -var Spdy = require('libp2p-spdy') +const multiaddr = require('multiaddr') +// const Id = require('peer-id') +const Peer = require('peer-info') +const Swarm = require('../src') +const TCP = require('libp2p-tcp') +const bl = require('bl') +// var SPDY = require('libp2p-spdy') -// because of Travis-CI -process.on('uncaughtException', function (err) { - console.log('Caught exception: ' + err) -}) - -describe('Basics', function () { - it('enforces creation with new', function (done) { - expect(function () { - Swarm() - }).to.throw() - done() - }) - - it('it throws an exception without peerSelf', function (done) { - expect(function () { - var sw = new Swarm() - sw.close() - }).to.throw(Error) +describe('basics', () => { + it('throws on missing peerInfo', (done) => { + expect(Swarm).to.throw(Error) done() }) }) -describe('When dialing', function () { - describe('if the swarm does add any of the peer transports', function () { - it('it returns an error', function (done) { - var peerOne = new Peer(Id.create(), [multiaddr('/ip4/127.0.0.1/tcp/8090')]) - var peerTwo = new Peer(Id.create(), [multiaddr('/ip4/127.0.0.1/tcp/8091')]) - var swarm = new Swarm(peerOne) +describe('transport - tcp', function () { + this.timeout(10000) - swarm.dial(peerTwo, {}, function (err) { - expect(err).to.exist - done() - }) - }) - }) -}) + var swarmA + var swarmB + var peerA = new Peer() + var peerB = new Peer() -describe('Without a Stream Muxer', function () { - describe('and one swarm over tcp', function () { - it('add the transport', function (done) { - var mh = multiaddr('/ip4/127.0.0.1/tcp/8010') - var p = new Peer(Id.create(), []) - var sw = new Swarm(p) - - sw.addTransport('tcp', tcp, { multiaddr: mh }, {}, {port: 8010}, ready) - - function ready () { - expect(sw.transports['tcp'].options).to.deep.equal({}) - expect(sw.transports['tcp'].dialOptions).to.deep.equal({}) - expect(sw.transports['tcp'].listenOptions).to.deep.equal({port: 8010}) - expect(sw.transports['tcp'].transport).to.deep.equal(tcp) - - sw.close(done) - } - }) + before((done) => { + peerA.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9888')) + peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9999')) + swarmA = new Swarm(peerA) + swarmB = new Swarm(peerB) + done() }) - describe('and two swarms over tcp', function () { - var mh1, p1, sw1, mh2, p2, sw2 - - beforeEach(function (done) { - mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010') - p1 = new Peer(Id.create(), []) - sw1 = new Swarm(p1) - - mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020') - p2 = new Peer(Id.create(), []) - sw2 = new Swarm(p2) - - async.parallel([ - function (cb) { - sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, cb) - }, - function (cb) { - sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, cb) - } - ], done) - }) - - afterEach(function (done) { - async.parallel([sw1.close, sw2.close], done) - }) - - it('dial a conn', function (done) { - sw1.dial(p2, {}, function (err) { - expect(err).to.equal(undefined) - expect(Object.keys(sw1.conns).length).to.equal(1) - done() - }) - }) - - it('dial a conn on a protocol', function (done) { - sw2.handleProtocol('/sparkles/1.0.0', function (conn) { - conn.end() - conn.on('end', done) - }) - - sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { - expect(err).to.equal(null) - expect(Object.keys(sw1.conns).length).to.equal(0) - conn.end() - }) - }) - - it('dial a protocol on a previous created conn', function (done) { - sw2.handleProtocol('/sparkles/1.0.0', function (conn) { - conn.end() - conn.on('end', done) - }) - - sw1.dial(p2, {}, function (err) { - expect(err).to.equal(undefined) - expect(Object.keys(sw1.conns).length).to.equal(1) - - sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { - expect(err).to.equal(null) - expect(Object.keys(sw1.conns).length).to.equal(0) - - conn.end() - }) - }) - }) - - // it('add an upgrade', function (done) { done() }) - // it('dial a conn on top of a upgrade', function (done) { done() }) - // it('dial a conn on a protocol on top of a upgrade', function (done) { done() }) - }) - - /* TODO - describe('udp', function () { - it('add the transport', function (done) { done() }) - it('dial a conn', function (done) { done() }) - it('dial a conn on a protocol', function (done) { done() }) - it('add an upgrade', function (done) { done() }) - it('dial a conn on top of a upgrade', function (done) { done() }) - it('dial a conn on a protocol on top of a upgrade', function (done) { done() }) - }) */ - - /* TODO - describe('udt', function () { - it('add the transport', function (done) { done() }) - it('dial a conn', function (done) { done() }) - it('dial a conn on a protocol', function (done) { done() }) - it('add an upgrade', function (done) { done() }) - it('dial a conn on top of a upgrade', function (done) { done() }) - it('dial a conn on a protocol on top of a upgrade', function (done) { done() }) - }) */ - -/* TODO -describe('utp', function () { - it('add the transport', function (done) { done() }) - it('dial a conn', function (done) { done() }) - it('dial a conn on a protocol', function (done) { done() }) - it('add an upgrade', function (done) { done() }) - it('dial a conn on top of a upgrade', function (done) { done() }) - it('dial a conn on a protocol on top of a upgrade', function (done) { done() }) -}) */ -}) - -describe('With a SPDY Stream Muxer', function () { - describe('and one swarm over tcp', function () { - // TODO: What is the it here? - it('add Stream Muxer', function (done) { - // var mh = multiaddr('/ip4/127.0.0.1/tcp/8010') - var p = new Peer(Id.create(), []) - var sw = new Swarm(p) - sw.addStreamMuxer('spdy', Spdy, {}) - + it('add', (done) => { + swarmA.transport.add('tcp', new TCP()) + expect(Object.keys(swarmA.transports).length).to.equal(1) + swarmB.transport.add('tcp', new TCP(), () => { + expect(Object.keys(swarmB.transports).length).to.equal(1) done() }) }) - describe('and two swarms over tcp', function () { - var mh1, p1, sw1, mh2, p2, sw2 - - beforeEach(function (done) { - mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010') - p1 = new Peer(Id.create(), []) - sw1 = new Swarm(p1) - sw1.addStreamMuxer('spdy', Spdy, {}) - - mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020') - p2 = new Peer(Id.create(), []) - sw2 = new Swarm(p2) - sw2.addStreamMuxer('spdy', Spdy, {}) - - async.parallel([ - function (cb) { - sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, cb) - }, - function (cb) { - sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, cb) - } - ], done) - }) - - function afterEach (done) { - var cleaningCounter = 0 - sw1.closeConns(cleaningUp) - sw2.closeConns(cleaningUp) - - sw1.closeListener('tcp', cleaningUp) - sw2.closeListener('tcp', cleaningUp) - - function cleaningUp () { - cleaningCounter++ - // TODO FIX: here should be 4, but because super wrapping of - // streams, it makes it so hard to properly close the muxed - // streams - https://github.com/indutny/spdy-transport/issues/14 - if (cleaningCounter < 3) { - return - } + it('listen', (done) => { + var count = 0 + swarmA.transport.listen('tcp', {}, (conn) => { + conn.pipe(conn) + }, ready) + swarmB.transport.listen('tcp', {}, (conn) => { + conn.pipe(conn) + }, ready) + function ready () { + if (++count === 2) { + expect(peerA.multiaddrs.length).to.equal(1) + expect(peerA.multiaddrs[0]).to.deep.equal(multiaddr('/ip4/127.0.0.1/tcp/9888')) + expect(peerB.multiaddrs.length).to.equal(1) + expect(peerB.multiaddrs[0]).to.deep.equal(multiaddr('/ip4/127.0.0.1/tcp/9999')) done() } } - - it('dial a conn on a protocol', function (done) { - sw2.handleProtocol('/sparkles/1.0.0', function (conn) { - // formallity so that the conn starts flowing - conn.on('data', function (chunk) {}) - - conn.end() - conn.on('end', function () { - expect(Object.keys(sw1.muxedConns).length).to.equal(1) - expect(Object.keys(sw2.muxedConns).length).to.equal(0) - afterEach(done) - }) - }) - - sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { - conn.on('data', function () {}) - expect(err).to.equal(null) - expect(Object.keys(sw1.conns).length).to.equal(0) - conn.end() - }) - }) - - it('dial two conns (transport reuse)', function (done) { - sw2.handleProtocol('/sparkles/1.0.0', function (conn) { - // formality so that the conn starts flowing - conn.on('data', function (chunk) {}) - - conn.end() - conn.on('end', function () { - expect(Object.keys(sw1.muxedConns).length).to.equal(1) - expect(Object.keys(sw2.muxedConns).length).to.equal(0) - - afterEach(done) - }) - }) - - sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { - // TODO Improve clarity - sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { - conn.on('data', function () {}) - expect(err).to.equal(null) - expect(Object.keys(sw1.conns).length).to.equal(0) - - conn.end() - }) - - conn.on('data', function () {}) - expect(err).to.equal(null) - expect(Object.keys(sw1.conns).length).to.equal(0) - - conn.end() - }) - }) }) - describe('and two identity enabled swarms over tcp', function () { - var mh1, p1, sw1, mh2, p2, sw2 - - beforeEach(function (done) { - mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010') - p1 = new Peer(Id.create(), []) - sw1 = new Swarm(p1) - sw1.addStreamMuxer('spdy', Spdy, {}) - sw1.enableIdentify() - - mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020') - p2 = new Peer(Id.create(), []) - sw2 = new Swarm(p2) - sw2.addStreamMuxer('spdy', Spdy, {}) - sw2.enableIdentify() - - async.parallel([ - function (cb) { - sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, cb) - }, - function (cb) { - sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, cb) - } - ], done) + it('dial to a multiaddr', (done) => { + const conn = swarmA.transport.dial('tcp', multiaddr('/ip4/127.0.0.1/tcp/9999'), (err, conn) => { + expect(err).to.not.exist }) + conn.pipe(bl((err, data) => { + expect(err).to.not.exist + done() + })) + conn.write('hey') + conn.end() + }) - afterEach(function (done) { - var cleaningCounter = 0 - sw1.closeConns(cleaningUp) - sw2.closeConns(cleaningUp) + it('dial to set of multiaddr, only one is available', (done) => { + const conn = swarmA.transport.dial('tcp', [ + multiaddr('/ip4/127.0.0.1/tcp/9910'), + multiaddr('/ip4/127.0.0.1/tcp/9999'), + multiaddr('/ip4/127.0.0.1/tcp/9309') + ], (err, conn) => { + expect(err).to.not.exist + }) + conn.pipe(bl((err, data) => { + expect(err).to.not.exist + done() + })) + conn.write('hey') + conn.end() + }) - sw1.closeListener('tcp', cleaningUp) - sw2.closeListener('tcp', cleaningUp) + it('close', (done) => { + var count = 0 + swarmA.transport.close('tcp', closed) + swarmB.transport.close('tcp', closed) - function cleaningUp () { - cleaningCounter++ - // TODO FIX: here should be 4, but because super wrapping of - // streams, it makes it so hard to properly close the muxed - // streams - https://github.com/indutny/spdy-transport/issues/14 - if (cleaningCounter < 3) { - return - } - // give time for identify to finish - setTimeout(function () { - expect(Object.keys(sw2.muxedConns).length).to.equal(1) - done() - }, 500) + function closed () { + if (++count === 2) { + done() } - }) + } + }) - it('identify', function (done) { - sw2.handleProtocol('/sparkles/1.0.0', function (conn) { - // formallity so that the conn starts flowing - conn.on('data', function (chunk) {}) + it('support port 0', (done) => { + var swarm + var peer = new Peer() + peer.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/0')) + swarm = new Swarm(peer) + swarm.transport.add('tcp', new TCP()) + swarm.transport.listen('tcp', {}, (conn) => { + conn.pipe(conn) + }, ready) - conn.end() - conn.on('end', function () { - expect(Object.keys(sw1.muxedConns).length).to.equal(1) - done() - }) - }) + function ready () { + expect(peer.multiaddrs.length).to.equal(1) + expect(peer.multiaddrs[0]).to.not.deep.equal(multiaddr('/ip4/127.0.0.1/tcp/0')) + swarm.close(done) + } + }) - sw1.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) { - conn.on('data', function () {}) - expect(err).to.equal(null) - expect(Object.keys(sw1.conns).length).to.equal(0) - conn.end() - }) - }) + it('support addr /ip4/0.0.0.0/tcp/9050', (done) => { + var swarm + var peer = new Peer() + peer.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/9050')) + swarm = new Swarm(peer) + swarm.transport.add('tcp', new TCP()) + swarm.transport.listen('tcp', {}, (conn) => { + conn.pipe(conn) + }, ready) + + function ready () { + expect(peer.multiaddrs.length).to.equal(1) + expect(peer.multiaddrs[0]).to.deep.equal(multiaddr('/ip4/0.0.0.0/tcp/9050')) + swarm.close(done) + } + }) + + it('support addr /ip4/0.0.0.0/tcp/0', (done) => { + var swarm + var peer = new Peer() + peer.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/0')) + swarm = new Swarm(peer) + swarm.transport.add('tcp', new TCP()) + swarm.transport.listen('tcp', {}, (conn) => { + conn.pipe(conn) + }, ready) + + function ready () { + expect(peer.multiaddrs.length).to.equal(1) + expect(peer.multiaddrs[0]).to.not.deep.equal(multiaddr('/ip4/0.0.0.0/tcp/0')) + swarm.close(done) + } + }) + + it('listen in several addrs', (done) => { + var swarm + var peer = new Peer() + peer.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9001')) + peer.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9002')) + peer.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9003')) + swarm = new Swarm(peer) + swarm.transport.add('tcp', new TCP()) + swarm.transport.listen('tcp', {}, (conn) => { + conn.pipe(conn) + }, ready) + + function ready () { + expect(peer.multiaddrs.length).to.equal(3) + swarm.close(done) + } }) }) + +describe('transport - udt', () => { + before((done) => { done() }) + + it.skip('add', (done) => {}) + it.skip('listen', (done) => {}) + it.skip('dial', (done) => {}) + it.skip('close', (done) => {}) +}) + +describe('transport - websockets', () => { + before((done) => { done() }) + + it.skip('add', (done) => {}) + it.skip('listen', (done) => {}) + it.skip('dial', (done) => {}) + it.skip('close', (done) => {}) +}) + +describe('high level API - 1st stage, without stream multiplexing (on TCP)', () => { + it.skip('handle a protocol', (done) => {}) + it.skip('dial on protocol', (done) => {}) + it.skip('dial to warm conn', (done) => {}) + it.skip('dial on protocol, reuse warmed conn', (done) => {}) + it.skip('close', (done) => {}) +}) + +describe('stream muxing (on TCP)', () => { + describe('multiplex', () => { + before((done) => { done() }) + it.skip('add', (done) => {}) + it.skip('handle + dial on protocol', (done) => {}) + it.skip('dial to warm conn', (done) => {}) + it.skip('dial on protocol, reuse warmed conn', (done) => {}) + it.skip('enable identify to reuse incomming muxed conn', (done) => {}) + it.skip('close', (done) => {}) + }) + describe('spdy', () => { + it.skip('add', (done) => {}) + it.skip('handle + dial on protocol', (done) => {}) + it.skip('dial to warm conn', (done) => {}) + it.skip('dial on protocol, reuse warmed conn', (done) => {}) + it.skip('enable identify to reuse incomming muxed conn', (done) => {}) + it.skip('close', (done) => {}) + }) +}) + +describe('conn upgrades', () => { + describe('secio on tcp', () => { + before((done) => { done() }) + + it.skip('add', (done) => {}) + it.skip('dial', (done) => {}) + it.skip('tls on a muxed stream (not the full conn)', (done) => {}) + }) + describe('tls on tcp', () => { + before((done) => { done() }) + + it.skip('add', (done) => {}) + it.skip('dial', (done) => {}) + it.skip('tls on a muxed stream (not the full conn)', (done) => {}) + }) +}) + +describe('high level API = 2nd stage, with everything all together!', () => { + before((done) => { done() }) + + it.skip('add tcp', (done) => {}) + it.skip('add utp', (done) => {}) + it.skip('add websockets', (done) => {}) + it.skip('dial', (done) => {}) +})