mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-05-23 07:31:21 +00:00
Merge pull request #10 from diasdavid/revisit
Revisit Swarm - multitransport + upgrades - https://github.com/diasdavid/node-ipfs-swarm/issues/8
This commit is contained in:
commit
4d9d8c94c7
85
README.md
85
README.md
@ -1,82 +1,69 @@
|
||||
ipfs-swarm Node.js implementation
|
||||
libp2p-swarm Node.js implementation
|
||||
=================================
|
||||
|
||||
[](http://ipn.io) [](http://ipfs.io/) [](http://webchat.freenode.net/?channels=%23ipfs) [](https://travis-ci.org/diasdavid/node-ipfs-swarm)
|
||||
|
||||
> IPFS swarm implementation in Node.js
|
||||
> libp2p swarm implementation in Node.js
|
||||
|
||||
# Description
|
||||
|
||||
ipfs-swarm is an abstraction for the network layer on IPFS. It offers an API to open streams between peers on a specific protocol.
|
||||
libp2p-swarm is a connection abstraction that is able to leverage several transports and connection upgrades, such as congestion control, channel encryption, multiplexing several streams in one connection, and more. It does this by bringing protocol multiplexing to the application level (instead of the traditional Port level) using multicodec and multistream.
|
||||
|
||||
Ref spec (WIP) - https://github.com/diasdavid/specs/blob/protocol-spec/protocol/layers.md#network-layer
|
||||
libp2p-swarm is used by libp2p but it can be also used as a standalone module.
|
||||
|
||||
# Usage
|
||||
|
||||
### Create a new Swarm
|
||||
### Install and create a Swarm
|
||||
|
||||
```javascript
|
||||
var Swarm = require('ipfs-swarm')
|
||||
libp2p-swarm is available on npm and so, like any other npm module, just:
|
||||
|
||||
var s = new Swarm([port]) // `port` defalts to 4001
|
||||
```bash
|
||||
$ npm install libp2p-swarm --save
|
||||
```
|
||||
|
||||
### Set the swarm to listen for incoming streams
|
||||
And use it in your Node.js code as:
|
||||
|
||||
```javascript
|
||||
s.listen([port], [callback]) // `port` defaults to 4001, `callback` gets called when the socket starts listening
|
||||
```JavaScript
|
||||
var Swarm = require('libp2p-swarm')
|
||||
|
||||
var sw = new Swarm(peerInfoSelf)
|
||||
```
|
||||
|
||||
### Close the listener/socket and every open stream that was multiplexed on it
|
||||
peerInfoSelf is a [PeerInfo](https://github.com/diasdavid/node-peer-info) object that represents the peer creating this swarm instance.
|
||||
|
||||
```javascript
|
||||
s.closeListener()
|
||||
### Support a transport
|
||||
|
||||
libp2p-swarm expects transports that implement [abstract-transport](https://github.com/diasdavid/abstract-transport). For example [libp2p-tcp](https://github.com/diasdavid/node-libp2p-tcp), a simple shim on top of the `net` module to make it work with swarm expectations.
|
||||
|
||||
```JavaScript
|
||||
sw.addTransport(transport, [options, dialOptions, listenOptions])
|
||||
```
|
||||
|
||||
### Register a protocol to be handled by an incoming stream
|
||||
### Add a connection upgrade
|
||||
|
||||
```javascript
|
||||
s.registerHandler('/name/protocol/you/want/version', function (stream) {})
|
||||
A connection upgrade must be able to receive and return something that implements the [abstract-connection](https://github.com/diasdavid/abstract-connection) interface.
|
||||
|
||||
```JavaScript
|
||||
sw.addUpgrade(connUpgrade, [options])
|
||||
```
|
||||
|
||||
### Open a new connection
|
||||
Upgrading a connection to use a stream muxer is still considered an upgrade, but a special case since once this connection is applied, the returned obj will implement the [abstract-stream-muxer](https://github.com/diasdavid/abstract-stream-muxer) interface.
|
||||
|
||||
Used when we want to make sure we can connect to a given peer, but do not intend to establish a stream with any of the services offered right away.
|
||||
|
||||
```
|
||||
s.openConnection(peerConnection, function (err) {})
|
||||
```JavaScript
|
||||
sw.addStreamMuxer(streamMuxer, [options])
|
||||
```
|
||||
|
||||
### Dial to another peer
|
||||
|
||||
### Dial a new stream
|
||||
|
||||
```
|
||||
s.openStream(peerInfo, protocol, function (err, stream) {})
|
||||
```JavaScript
|
||||
sw.dial(PeerInfo, options, protocol)
|
||||
sw.dial(PeerInfo, options)
|
||||
```
|
||||
|
||||
peerInfo must be a [`ipfs-peer`](https://www.npmjs.com/package/ipfs-peer) object, contaning both peer-id and multiaddrs.
|
||||
dial uses the best transport (whatever works first, in the future we can have some criteria), and jump starts the connection until the point we have to negotiate the protocol. If a muxer is available, then drop the muxer onto that connection. Good to warm up connections or to check for connectivity. If we have already a muxer for that peerInfo, than do nothing.
|
||||
|
||||
## Events emitted
|
||||
### Accept requests on a specific protocol
|
||||
|
||||
```JavaScript
|
||||
sw.handleProtocol(protocol, handlerFunction)
|
||||
```
|
||||
.on('error')
|
||||
|
||||
.on('connection')
|
||||
.on('connection-unknown') // used by Identify to start the Identify protocol from listener to dialer
|
||||
```
|
||||
|
||||
## Identify protocol
|
||||
|
||||
The Identify protocol is an integral part to Swarm. It enables peers to share observedAddrs, identities and other possible address available. This enables us to do better NAT traversal.
|
||||
|
||||
To instantiate Identify:
|
||||
|
||||
```
|
||||
var Identify = require('ipfs-swarm/identify')
|
||||
|
||||
var i = new Identify(swarmInstance, peerSelf)
|
||||
```
|
||||
|
||||
`swarmInstance` must be an Instance of swarm and `peerSelf` must be a instance of `ipfs-peer` that represents the peer that instantiated this Identify
|
||||
|
||||
Identify emits a `peer-update` event each time it receives information from another peer.
|
||||
|
14
examples/peerB.js
Normal file
14
examples/peerB.js
Normal file
@ -0,0 +1,14 @@
|
||||
var Swarm = require('./../src')
|
||||
|
||||
var Peer = require('peer-info')
|
||||
var Id = require('peer-id')
|
||||
var multiaddr = require('multiaddr')
|
||||
var tcp = require('libp2p-tcp')
|
||||
|
||||
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}, function () {
|
||||
console.log('transport added')
|
||||
})
|
@ -1,28 +0,0 @@
|
||||
// var Identify = require('./../src/identify')
|
||||
var Swarm = require('./../src')
|
||||
// var Peer = require('ipfs-peer')
|
||||
// var Id = require('ipfs-peer-id')
|
||||
// var multiaddr = require('multiaddr')
|
||||
|
||||
var b = new Swarm()
|
||||
b.port = 4001
|
||||
// var peerB = new Peer(Id.create(), [multiaddr('/ip4/127.0.0.1/tcp/' + b.port)])
|
||||
|
||||
// var i = new Identify(b, peerB)
|
||||
// i.on('thenews', function (news) {
|
||||
// console.log('such news')
|
||||
// })
|
||||
|
||||
b.on('error', function (err) {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
b.listen()
|
||||
|
||||
b.registerHandler('/ipfs/sparkles/1.2.3', function (stream) {
|
||||
// if (err) {
|
||||
// return console.log(err)
|
||||
// }
|
||||
|
||||
console.log('woop got a stream')
|
||||
})
|
19
package.json
19
package.json
@ -1,14 +1,12 @@
|
||||
{
|
||||
"name": "ipfs-swarm",
|
||||
"name": "libp2p-swarm",
|
||||
"version": "0.4.1",
|
||||
"description": "IPFS swarm implementation in Node.js",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/lab tests/*-test.js",
|
||||
"coverage": "./node_modules/.bin/lab -t 100 tests/*-test.js",
|
||||
"codestyle": "./node_modules/.bin/standard --format",
|
||||
"lint": "./node_modules/.bin/standard",
|
||||
"validate": "npm ls"
|
||||
"coverage": "./node_modules/.bin/lab -t 88 tests/*-test.js",
|
||||
"lint": "./node_modules/.bin/standard"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -24,8 +22,9 @@
|
||||
},
|
||||
"homepage": "https://github.com/diasdavid/node-ipfs-swarm",
|
||||
"pre-commit": [
|
||||
"codestyle",
|
||||
"test"
|
||||
"lint",
|
||||
"test",
|
||||
"coverage"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^4.0.0"
|
||||
@ -33,6 +32,8 @@
|
||||
"devDependencies": {
|
||||
"code": "^1.4.1",
|
||||
"lab": "^5.13.0",
|
||||
"libp2p-spdy": "^0.1.0",
|
||||
"libp2p-tcp": "^0.1.1",
|
||||
"precommit-hook": "^3.0.0",
|
||||
"sinon": "^1.15.4",
|
||||
"standard": "^4.5.2",
|
||||
@ -42,11 +43,11 @@
|
||||
"async": "^1.3.0",
|
||||
"ip-address": "^4.0.0",
|
||||
"ipfs-logger": "^0.1.0",
|
||||
"ipfs-peer": "^0.3.0",
|
||||
"ipfs-peer-id": "^0.3.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",
|
||||
"spdy-stream-muxer": "^0.6.0"
|
||||
}
|
||||
|
@ -4,75 +4,48 @@
|
||||
*/
|
||||
|
||||
var Interactive = require('multistream-select').Interactive
|
||||
var EventEmmiter = require('events').EventEmitter
|
||||
var util = require('util')
|
||||
var protobufs = require('protocol-buffers-stream')
|
||||
var fs = require('fs')
|
||||
var schema = fs.readFileSync(__dirname + '/identify.proto')
|
||||
var v6 = require('ip-address').v6
|
||||
var Id = require('ipfs-peer-id')
|
||||
var Id = require('peer-id')
|
||||
var multiaddr = require('multiaddr')
|
||||
|
||||
exports = module.exports = Identify
|
||||
exports = module.exports = identify
|
||||
|
||||
util.inherits(Identify, EventEmmiter)
|
||||
var protoId = '/ipfs/identify/1.0.0'
|
||||
|
||||
function Identify (swarm, peerSelf) {
|
||||
var self = this
|
||||
self.createProtoStream = protobufs(schema)
|
||||
exports.protoId = protoId
|
||||
var createProtoStream = protobufs(schema)
|
||||
|
||||
swarm.registerHandler('/ipfs/identify/1.0.0', function (stream) {
|
||||
var ps = self.createProtoStream()
|
||||
|
||||
ps.on('identify', function (msg) {
|
||||
updateSelf(peerSelf, msg.observedAddr)
|
||||
|
||||
var peerId = Id.createFromPubKey(msg.publicKey)
|
||||
|
||||
var socket = swarm.connections[peerId.toB58String()].socket
|
||||
var mh = getMultiaddr(socket)
|
||||
ps.identify({
|
||||
protocolVersion: 'na',
|
||||
agentVersion: 'na',
|
||||
publicKey: peerSelf.id.pubKey,
|
||||
listenAddrs: peerSelf.multiaddrs.map(function (mh) {return mh.buffer}),
|
||||
observedAddr: mh.buffer
|
||||
})
|
||||
|
||||
self.emit('peer-update', {
|
||||
peerId: peerId,
|
||||
listenAddrs: msg.listenAddrs.map(function (mhb) {return multiaddr(mhb)})
|
||||
})
|
||||
|
||||
ps.finalize()
|
||||
})
|
||||
ps.pipe(stream).pipe(ps)
|
||||
})
|
||||
|
||||
swarm.on('connection-unknown', function (conn, socket) {
|
||||
conn.dialStream(function (err, stream) {
|
||||
if (err) { return console.log(err) }
|
||||
function identify (muxedConns, peerInfoSelf, socket, conn, muxer) {
|
||||
var msi = new Interactive()
|
||||
msi.handle(stream, function () {
|
||||
msi.select('/ipfs/identify/1.0.0', function (err, ds) {
|
||||
if (err) { return console.log(err) }
|
||||
msi.handle(conn, function () {
|
||||
msi.select(protoId, function (err, ds) {
|
||||
if (err) {
|
||||
return console.log(err) // TODO Treat error
|
||||
}
|
||||
|
||||
var ps = self.createProtoStream()
|
||||
var ps = createProtoStream()
|
||||
|
||||
ps.on('identify', function (msg) {
|
||||
var peerId = Id.createFromPubKey(msg.publicKey)
|
||||
|
||||
updateSelf(peerSelf, msg.observedAddr)
|
||||
updateSelf(peerInfoSelf, msg.observedAddr)
|
||||
|
||||
swarm.connections[peerId.toB58String()] = {
|
||||
conn: conn,
|
||||
muxedConns[peerId.toB58String()] = {
|
||||
muxer: muxer,
|
||||
socket: socket
|
||||
}
|
||||
|
||||
self.emit('peer-update', {
|
||||
peerId: peerId,
|
||||
listenAddrs: msg.listenAddrs.map(function (mhb) {return multiaddr(mhb)})
|
||||
})
|
||||
// TODO: Pass the new discovered info about the peer that contacted us
|
||||
// to something like the Kademlia Router, so the peerInfo for this peer
|
||||
// is fresh
|
||||
// - before this was exectued through a event emitter
|
||||
// self.emit('peer-update', {
|
||||
// peerId: peerId,
|
||||
// listenAddrs: msg.listenAddrs.map(function (mhb) {return multiaddr(mhb)})
|
||||
// })
|
||||
})
|
||||
|
||||
var mh = getMultiaddr(socket)
|
||||
@ -80,8 +53,10 @@ function Identify (swarm, peerSelf) {
|
||||
ps.identify({
|
||||
protocolVersion: 'na',
|
||||
agentVersion: 'na',
|
||||
publicKey: peerSelf.id.pubKey,
|
||||
listenAddrs: peerSelf.multiaddrs.map(function (mh) {return mh.buffer}),
|
||||
publicKey: peerInfoSelf.id.pubKey,
|
||||
listenAddrs: peerInfoSelf.multiaddrs.map(function (mh) {
|
||||
return mh.buffer
|
||||
}),
|
||||
observedAddr: mh.buffer
|
||||
})
|
||||
|
||||
@ -89,8 +64,50 @@ function Identify (swarm, peerSelf) {
|
||||
ps.finalize()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
exports.getHandlerFunction = function (peerInfoSelf, muxedConns) {
|
||||
return function (conn) {
|
||||
// wait for the other peer to identify itself
|
||||
// update our multiaddr with observed addr list
|
||||
// then get the socket from our list of muxedConns and send the reply back
|
||||
|
||||
var ps = createProtoStream()
|
||||
|
||||
ps.on('identify', function (msg) {
|
||||
updateSelf(peerInfoSelf, msg.observedAddr)
|
||||
|
||||
var peerId = Id.createFromPubKey(msg.publicKey)
|
||||
|
||||
var socket = muxedConns[peerId.toB58String()].socket
|
||||
|
||||
var mh = getMultiaddr(socket)
|
||||
|
||||
ps.identify({
|
||||
protocolVersion: 'na',
|
||||
agentVersion: 'na',
|
||||
publicKey: peerInfoSelf.id.pubKey,
|
||||
listenAddrs: peerInfoSelf.multiaddrs.map(function (mh) {
|
||||
return mh.buffer
|
||||
}),
|
||||
observedAddr: mh.buffer
|
||||
})
|
||||
|
||||
// TODO: Pass the new discovered info about the peer that contacted us
|
||||
// to something like the Kademlia Router, so the peerInfo for this peer
|
||||
// is fresh
|
||||
// - before this was exectued through a event emitter
|
||||
// self.emit('peer-update', {
|
||||
// peerId: peerId,
|
||||
// listenAddrs: msg.listenAddrs.map(function (mhb) {
|
||||
// return multiaddr(mhb)
|
||||
// })
|
||||
// })
|
||||
|
||||
ps.finalize()
|
||||
})
|
||||
ps.pipe(conn).pipe(ps)
|
||||
}
|
||||
}
|
||||
|
||||
function getMultiaddr (socket) {
|
||||
|
@ -1,2 +0,0 @@
|
||||
exports = module.exports = require('spdy-stream-muxer')
|
||||
// exports = module.exports = require('multiplex-stream-muxer')
|
412
src/swarm.js
412
src/swarm.js
@ -1,189 +1,313 @@
|
||||
var tcp = require('net')
|
||||
var Select = require('multistream-select').Select
|
||||
var Interactive = require('multistream-select').Interactive
|
||||
var Muxer = require('./stream-muxer')
|
||||
var log = require('ipfs-logger').group('swarm')
|
||||
var multistream = require('multistream-select')
|
||||
var async = require('async')
|
||||
var EventEmitter = require('events').EventEmitter
|
||||
var util = require('util')
|
||||
var identify = require('./identify')
|
||||
|
||||
exports = module.exports = Swarm
|
||||
|
||||
util.inherits(Swarm, EventEmitter)
|
||||
|
||||
function Swarm () {
|
||||
function Swarm (peerInfo) {
|
||||
var self = this
|
||||
|
||||
if (!(self instanceof Swarm)) {
|
||||
throw new Error('Swarm must be called with new')
|
||||
}
|
||||
|
||||
self.port = parseInt(process.env.IPFS_SWARM_PORT, 10) || 4001
|
||||
self.connections = {} // {peerIdB58: {conn: <>, socket: <>}
|
||||
self.handles = {}
|
||||
self.peerInfo = peerInfo
|
||||
|
||||
// set the listener
|
||||
// peerIdB58: { conn: <conn> }
|
||||
self.conns = {}
|
||||
|
||||
self.listen = function (port, ready) {
|
||||
if (!ready) {
|
||||
ready = function noop () {}
|
||||
// peerIdB58: {
|
||||
// muxer: <muxer>,
|
||||
// socket: socket // so we can extract the info we need for identify
|
||||
// }
|
||||
self.muxedConns = {}
|
||||
|
||||
// transportName: { transport: transport,
|
||||
// dialOptions: dialOptions,
|
||||
// listenOptions: listenOptions,
|
||||
// listeners: [] }
|
||||
self.transports = {}
|
||||
|
||||
// transportName: listener
|
||||
self.listeners = {}
|
||||
|
||||
// protocolName: handlerFunc
|
||||
self.protocols = {}
|
||||
|
||||
// muxerName: { Muxer: Muxer // Muxer is a constructor
|
||||
// options: options }
|
||||
self.muxers = {}
|
||||
|
||||
// for connection reuse
|
||||
self.identify = false
|
||||
|
||||
// public interface
|
||||
|
||||
self.addTransport = function (name, transport, options, dialOptions, listenOptions, callback) {
|
||||
// set up the transport and add the list of incoming streams
|
||||
// add transport to the list of transports
|
||||
|
||||
var listener = transport.createListener(options, listen)
|
||||
|
||||
listener.listen(listenOptions, function ready () {
|
||||
self.transports[name] = {
|
||||
transport: transport,
|
||||
options: options,
|
||||
dialOptions: dialOptions,
|
||||
listenOptions: listenOptions,
|
||||
listener: listener
|
||||
}
|
||||
if (typeof port === 'function') {
|
||||
ready = port
|
||||
} else if (port) { self.port = port }
|
||||
|
||||
//
|
||||
// If a known multiaddr is passed, then add to our list of multiaddrs
|
||||
if (options.multiaddr) {
|
||||
self.peerInfo.multiaddrs.push(options.multiaddr)
|
||||
}
|
||||
|
||||
self.listener = tcp.createServer(function (socket) {
|
||||
errorUp(self, socket)
|
||||
var ms = new Select()
|
||||
ms.handle(socket)
|
||||
ms.addHandler('/spdy/3.1.0', function (ds) {
|
||||
log.info('Negotiated spdy with incoming socket')
|
||||
|
||||
var conn = new Muxer().attach(ds, true)
|
||||
|
||||
// attach multistream handlers to incoming streams
|
||||
|
||||
conn.on('stream', registerHandles)
|
||||
errorUp(self, conn)
|
||||
|
||||
// FOR IDENTIFY
|
||||
self.emit('connection-unknown', conn, socket)
|
||||
|
||||
// IDENTIFY DOES THIS FOR US
|
||||
// conn.on('close', function () { delete self.connections[conn.peerId] })
|
||||
callback()
|
||||
})
|
||||
}).listen(self.port, ready)
|
||||
errorUp(self, self.listener)
|
||||
}
|
||||
|
||||
// interface
|
||||
self.addUpgrade = function (ConnUpgrade, options) {}
|
||||
|
||||
// open stream account for connection reuse
|
||||
self.openConnection = function (peer, cb) {
|
||||
// If no connection open yet, open it
|
||||
if (!self.connections[peer.id.toB58String()]) {
|
||||
// Establish a socket with one of the addresses
|
||||
var socket
|
||||
async.eachSeries(peer.multiaddrs, function (multiaddr, next) {
|
||||
if (socket) { return next() }
|
||||
self.addStreamMuxer = function (name, StreamMuxer, options) {
|
||||
self.muxers[name] = {
|
||||
Muxer: StreamMuxer,
|
||||
options: options
|
||||
}
|
||||
}
|
||||
|
||||
var tmp = tcp.connect(multiaddr.toOptions(), function () {
|
||||
socket = tmp
|
||||
errorUp(self, socket)
|
||||
self.dial = function (peerInfo, options, protocol, callback) {
|
||||
// 1. check if we have transports we support
|
||||
// 2. check if we have a conn waiting
|
||||
// 3. check if we have a stream muxer available
|
||||
|
||||
if (typeof protocol === 'function') {
|
||||
callback = protocol
|
||||
protocol = undefined
|
||||
}
|
||||
|
||||
// check if a conn is waiting
|
||||
// if it is and protocol was selected, jump into multistreamHandshake
|
||||
// if it is and no protocol was selected, do nothing and call and empty callback
|
||||
|
||||
if (self.conns[peerInfo.id.toB58String()]) {
|
||||
if (protocol) {
|
||||
if (self.muxers['spdy']) {
|
||||
// TODO upgrade this conn to a muxer
|
||||
console.log('TODO: upgrade a warm conn to muxer that was added after')
|
||||
} else {
|
||||
multistreamHandshake(self.conns[peerInfo.id.toB58String()])
|
||||
}
|
||||
self.conns[peerInfo.id.toB58String()] = undefined
|
||||
delete self.conns[peerInfo.id.toB58String()]
|
||||
return
|
||||
} else {
|
||||
return callback()
|
||||
}
|
||||
}
|
||||
|
||||
// check if a stream muxer for this peer is available
|
||||
if (self.muxedConns[peerInfo.id.toB58String()]) {
|
||||
if (protocol) {
|
||||
return openMuxedStream(self.muxedConns[peerInfo.id.toB58String()].muxer)
|
||||
} else {
|
||||
return callback()
|
||||
}
|
||||
}
|
||||
|
||||
// Creating a new conn with this peer routine
|
||||
|
||||
// TODO - check if there is a preference in protocol to use on
|
||||
// options.protocol
|
||||
var supportedTransports = Object.keys(self.transports)
|
||||
var multiaddrs = peerInfo.multiaddrs.filter(function (multiaddr) {
|
||||
return multiaddr.protoNames().some(function (proto) {
|
||||
return supportedTransports.indexOf(proto) >= 0
|
||||
})
|
||||
})
|
||||
|
||||
var conn
|
||||
|
||||
async.eachSeries(multiaddrs, function (multiaddr, next) {
|
||||
if (conn) {
|
||||
return next()
|
||||
}
|
||||
|
||||
var transportName = getTransportNameForMultiaddr(multiaddr)
|
||||
var transport = self.transports[transportName]
|
||||
var dialOptions = clone(transport.dialOptions)
|
||||
dialOptions.ready = connected
|
||||
|
||||
var connTry = transport.transport.dial(multiaddr, dialOptions)
|
||||
|
||||
connTry.once('error', function (err) {
|
||||
if (err) {
|
||||
return console.log(err) // TODO handle error
|
||||
}
|
||||
next() // try next multiaddr
|
||||
})
|
||||
|
||||
function connected () {
|
||||
conn = connTry
|
||||
next()
|
||||
})
|
||||
|
||||
tmp.once('error', function (err) {
|
||||
log.warn(multiaddr.toString(), 'on', peer.id.toB58String(), 'not available', err)
|
||||
next()
|
||||
})
|
||||
|
||||
}, function done () {
|
||||
if (!socket) {
|
||||
return cb(new Error('Not able to open a scoket with peer - ',
|
||||
peer.id.toB58String()))
|
||||
}
|
||||
gotSocket(socket)
|
||||
|
||||
function getTransportNameForMultiaddr (multiaddr) {
|
||||
// this works for all those ip + transport + port tripplets
|
||||
return multiaddr.protoNames()[1]
|
||||
}
|
||||
|
||||
function clone (obj) {
|
||||
var target = {}
|
||||
for (var i in obj) {
|
||||
if (obj.hasOwnProperty(i)) {
|
||||
target[i] = obj[i]
|
||||
}
|
||||
}
|
||||
return target
|
||||
}
|
||||
}, done)
|
||||
|
||||
function done () {
|
||||
// TODO apply upgrades
|
||||
// apply stream muxer
|
||||
// if no protocol is selected, save it in the pool
|
||||
// if protocol is selected, multistream that protocol
|
||||
if (!conn) {
|
||||
callback(new Error('Unable to open a connection'))
|
||||
}
|
||||
|
||||
if (self.muxers['spdy']) {
|
||||
var spdy = new self.muxers['spdy'].Muxer(self.muxers['spdy'].options)
|
||||
spdy.attach(conn, false, function (err, muxer) {
|
||||
if (err) {
|
||||
return console.log(err) // TODO Treat error
|
||||
}
|
||||
|
||||
muxer.on('stream', userProtocolMuxer)
|
||||
|
||||
self.muxedConns[peerInfo.id.toB58String()] = {
|
||||
muxer: muxer,
|
||||
socket: conn
|
||||
}
|
||||
|
||||
if (protocol) {
|
||||
openMuxedStream(muxer)
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
cb()
|
||||
if (protocol) {
|
||||
multistreamHandshake(conn)
|
||||
} else {
|
||||
self.conns[peerInfo.id.toB58String()] = conn
|
||||
callback()
|
||||
}
|
||||
|
||||
// do the spdy people dance (multistream-select into spdy)
|
||||
function gotSocket (socket) {
|
||||
var msi = new Interactive()
|
||||
msi.handle(socket, function () {
|
||||
msi.select('/spdy/3.1.0', function (err, ds) {
|
||||
if (err) { cb(err) }
|
||||
|
||||
var conn = new Muxer().attach(ds, false)
|
||||
conn.on('stream', registerHandles)
|
||||
self.connections[peer.id.toB58String()] = {
|
||||
conn: conn,
|
||||
socket: socket
|
||||
}
|
||||
conn.on('close', function () { delete self.connections[peer.id.toB58String()]})
|
||||
errorUp(self, conn)
|
||||
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
self.openStream = function (peer, protocol, cb) {
|
||||
self.openConnection(peer, function (err) {
|
||||
function openMuxedStream (muxer) {
|
||||
// 1. create a new stream on this muxedConn and pass that to
|
||||
// multistreamHanshake
|
||||
muxer.dialStream(function (err, conn) {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
return console.log(err) // TODO Treat error
|
||||
}
|
||||
// spawn new muxed stream
|
||||
var conn = self.connections[peer.id.toB58String()].conn
|
||||
conn.dialStream(function (err, stream) {
|
||||
if (err) { return cb(err) }
|
||||
errorUp(self, stream)
|
||||
// negotiate desired protocol
|
||||
var msi = new Interactive()
|
||||
msi.handle(stream, function () {
|
||||
msi.select(protocol, function (err, ds) {
|
||||
if (err) { return cb(err) }
|
||||
peer.lastSeen = new Date()
|
||||
cb(null, ds) // return the stream
|
||||
})
|
||||
})
|
||||
})
|
||||
multistreamHandshake(conn)
|
||||
})
|
||||
}
|
||||
|
||||
self.registerHandler = function (protocol, handlerFunc) {
|
||||
if (self.handles[protocol]) {
|
||||
return handlerFunc(new Error('Handle for protocol already exists', protocol))
|
||||
function multistreamHandshake (conn) {
|
||||
var msI = new multistream.Interactive()
|
||||
msI.handle(conn, function () {
|
||||
msI.select(protocol, callback)
|
||||
})
|
||||
}
|
||||
self.handles[protocol] = handlerFunc
|
||||
log.info('Registered handler for protocol:', protocol)
|
||||
}
|
||||
|
||||
self.closeConns = function (cb) {
|
||||
var keys = Object.keys(self.connections)
|
||||
var number = keys.length
|
||||
if (number === 0) { cb() }
|
||||
var c = new Counter(number, cb)
|
||||
self.closeListener = function (transportName, callback) {
|
||||
self.transports[transportName].listener.close(closed)
|
||||
|
||||
keys.forEach(function (key) {
|
||||
self.connections[key].conn.end()
|
||||
c.hit()
|
||||
// only gets called when all the streams on this transport are closed too
|
||||
function closed () {
|
||||
delete self.transports[transportName]
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
self.closeConns = function (callback) {
|
||||
// close warmed up cons so that the listener can gracefully exit
|
||||
Object.keys(self.conns).forEach(function (conn) {
|
||||
self.conns[conn].destroy()
|
||||
})
|
||||
self.conns = {}
|
||||
|
||||
callback()
|
||||
}
|
||||
|
||||
self.close = function (callback) {
|
||||
// close everything
|
||||
}
|
||||
|
||||
self.enableIdentify = function () {
|
||||
// set flag to true
|
||||
// add identify to the list of handled protocols
|
||||
self.identify = true
|
||||
|
||||
// we pass muxedConns so that identify can access the socket of the other
|
||||
// peer
|
||||
self.handleProtocol(identify.protoId,
|
||||
identify.getHandlerFunction(self.peerInfo, self.muxedConns))
|
||||
}
|
||||
|
||||
self.handleProtocol = function (protocol, handlerFunction) {
|
||||
self.protocols[protocol] = handlerFunction
|
||||
}
|
||||
|
||||
// internals
|
||||
|
||||
function listen (conn) {
|
||||
// TODO apply upgrades
|
||||
// add StreamMuxer if available (and point streams from muxer to userProtocolMuxer)
|
||||
|
||||
if (self.muxers['spdy']) {
|
||||
var spdy = new self.muxers['spdy'].Muxer(self.muxers['spdy'].options)
|
||||
spdy.attach(conn, true, function (err, muxer) {
|
||||
if (err) {
|
||||
return console.log(err) // TODO treat error
|
||||
}
|
||||
|
||||
// TODO This muxer has to be identified!
|
||||
// pass to identify a reference of
|
||||
// our muxedConn list
|
||||
// ourselves (peerInfo)
|
||||
// the conn, which is the socket
|
||||
// and a stream it can send stuff
|
||||
if (self.identify) {
|
||||
muxer.dialStream(function (err, stream) {
|
||||
if (err) {
|
||||
return console.log(err) // TODO Treat error
|
||||
}
|
||||
// conn === socket at this point
|
||||
identify(self.muxedConns, self.peerInfo, conn, stream, muxer)
|
||||
})
|
||||
}
|
||||
|
||||
self.closeListener = function (cb) {
|
||||
self.listener.close(cb)
|
||||
muxer.on('stream', userProtocolMuxer)
|
||||
})
|
||||
} else {
|
||||
// if no stream muxer, then
|
||||
userProtocolMuxer(conn)
|
||||
}
|
||||
}
|
||||
|
||||
function registerHandles (stream) {
|
||||
log.info('Registering protocol handlers on new stream')
|
||||
errorUp(self, stream)
|
||||
var msH = new Select()
|
||||
msH.handle(stream)
|
||||
Object.keys(self.handles).forEach(function (protocol) {
|
||||
msH.addHandler(protocol, self.handles[protocol])
|
||||
// Handle user given protocols
|
||||
function userProtocolMuxer (conn) {
|
||||
var msS = new multistream.Select()
|
||||
msS.handle(conn)
|
||||
Object.keys(self.protocols).forEach(function (protocol) {
|
||||
msS.addHandler(protocol, self.protocols[protocol])
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function errorUp (self, emitter) {
|
||||
emitter.on('error', function (err) {
|
||||
self.emit('error', err)
|
||||
})
|
||||
}
|
||||
|
||||
function Counter (target, callback) {
|
||||
var c = 0
|
||||
this.hit = count
|
||||
|
||||
function count () {
|
||||
c += 1
|
||||
if (c === target) { callback() }
|
||||
}
|
||||
}
|
||||
|
@ -1,254 +1,426 @@
|
||||
var Lab = require('lab')
|
||||
var Code = require('code')
|
||||
var sinon = require('sinon')
|
||||
var lab = exports.lab = Lab.script()
|
||||
|
||||
var experiment = lab.experiment
|
||||
var test = lab.test
|
||||
var beforeEach = lab.beforeEach
|
||||
var afterEach = lab.afterEach
|
||||
var expect = Code.expect
|
||||
|
||||
var multiaddr = require('multiaddr')
|
||||
var Id = require('ipfs-peer-id')
|
||||
var Peer = require('ipfs-peer')
|
||||
var Swarm = require('../src/')
|
||||
var Identify = require('../src/identify')
|
||||
var Id = require('peer-id')
|
||||
var Peer = require('peer-info')
|
||||
var Swarm = require('../src')
|
||||
var tcp = require('libp2p-tcp')
|
||||
var Spdy = require('libp2p-spdy')
|
||||
|
||||
var swarmA
|
||||
var swarmB
|
||||
var peerA
|
||||
var peerB
|
||||
|
||||
beforeEach(function (done) {
|
||||
swarmA = new Swarm()
|
||||
swarmB = new Swarm()
|
||||
var c = new Counter(2, done)
|
||||
|
||||
swarmA.listen(8100, function () {
|
||||
peerA = new Peer(Id.create(), [multiaddr('/ip4/127.0.0.1/tcp/' + swarmA.port)])
|
||||
c.hit()
|
||||
})
|
||||
|
||||
swarmB.listen(8101, function () {
|
||||
peerB = new Peer(Id.create(), [multiaddr('/ip4/127.0.0.1/tcp/' + swarmB.port)])
|
||||
c.hit()
|
||||
})
|
||||
/* TODO
|
||||
experiment('Basics', function () {
|
||||
test('enforces creation with new', function (done) {done() })
|
||||
})
|
||||
*/
|
||||
|
||||
// because of Travis-CI
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.log('Caught exception: ' + err)
|
||||
})
|
||||
|
||||
afterEach(function (done) {
|
||||
// This should be 2, but for some reason
|
||||
// that will fail in most of the tests
|
||||
var c = new Counter(1, done)
|
||||
experiment('Without a Stream Muxer', function () {
|
||||
experiment('tcp', function () {
|
||||
test('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)
|
||||
|
||||
swarmA.closeListener(function () {
|
||||
c.hit()
|
||||
sw.addTransport('tcp', tcp,
|
||||
{ multiaddr: mh }, {}, {port: 8010}, function () {
|
||||
expect(sw.transports['tcp'].options).to.deep.equal({ multiaddr: mh })
|
||||
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.closeListener('tcp', function () {
|
||||
done()
|
||||
})
|
||||
swarmB.closeListener(function () {
|
||||
c.hit()
|
||||
})
|
||||
})
|
||||
|
||||
test('dial a conn', function (done) {
|
||||
var mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010')
|
||||
var p1 = new Peer(Id.create(), [])
|
||||
var sw1 = new Swarm(p1)
|
||||
sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, ready)
|
||||
|
||||
var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
|
||||
var p2 = new Peer(Id.create(), [])
|
||||
var sw2 = new Swarm(p2)
|
||||
sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, ready)
|
||||
|
||||
var readyCounter = 0
|
||||
|
||||
function ready () {
|
||||
readyCounter++
|
||||
if (readyCounter < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
sw1.dial(p2, {}, function (err) {
|
||||
expect(err).to.equal(undefined)
|
||||
expect(Object.keys(sw1.conns).length).to.equal(1)
|
||||
var cleaningCounter = 0
|
||||
sw1.closeConns(cleaningUp)
|
||||
sw2.closeConns(cleaningUp)
|
||||
|
||||
sw1.closeListener('tcp', cleaningUp)
|
||||
sw2.closeListener('tcp', cleaningUp)
|
||||
|
||||
function cleaningUp () {
|
||||
cleaningCounter++
|
||||
if (cleaningCounter < 4) {
|
||||
return
|
||||
}
|
||||
|
||||
done()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('dial a conn on a protocol', function (done) {
|
||||
var mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010')
|
||||
var p1 = new Peer(Id.create(), [])
|
||||
var sw1 = new Swarm(p1)
|
||||
sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, ready)
|
||||
|
||||
var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
|
||||
var p2 = new Peer(Id.create(), [])
|
||||
var sw2 = new Swarm(p2)
|
||||
sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, ready)
|
||||
|
||||
sw2.handleProtocol('/sparkles/1.0.0', function (conn) {
|
||||
conn.end()
|
||||
conn.on('end', function () {
|
||||
var cleaningCounter = 0
|
||||
sw1.closeConns(cleaningUp)
|
||||
sw2.closeConns(cleaningUp)
|
||||
|
||||
sw1.closeListener('tcp', cleaningUp)
|
||||
sw2.closeListener('tcp', cleaningUp)
|
||||
|
||||
function cleaningUp () {
|
||||
cleaningCounter++
|
||||
if (cleaningCounter < 4) {
|
||||
return
|
||||
}
|
||||
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
var count = 0
|
||||
|
||||
function ready () {
|
||||
count++
|
||||
if (count < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('dial a protocol on a previous created conn', function (done) {
|
||||
var mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010')
|
||||
var p1 = new Peer(Id.create(), [])
|
||||
var sw1 = new Swarm(p1)
|
||||
sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, ready)
|
||||
|
||||
var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
|
||||
var p2 = new Peer(Id.create(), [])
|
||||
var sw2 = new Swarm(p2)
|
||||
sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, ready)
|
||||
|
||||
var readyCounter = 0
|
||||
|
||||
sw2.handleProtocol('/sparkles/1.0.0', function (conn) {
|
||||
conn.end()
|
||||
conn.on('end', function () {
|
||||
var cleaningCounter = 0
|
||||
sw1.closeConns(cleaningUp)
|
||||
sw2.closeConns(cleaningUp)
|
||||
|
||||
sw1.closeListener('tcp', cleaningUp)
|
||||
sw2.closeListener('tcp', cleaningUp)
|
||||
|
||||
function cleaningUp () {
|
||||
cleaningCounter++
|
||||
if (cleaningCounter < 4) {
|
||||
return
|
||||
}
|
||||
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function ready () {
|
||||
readyCounter++
|
||||
if (readyCounter < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// test('add an upgrade', function (done) { done() })
|
||||
// test('dial a conn on top of a upgrade', function (done) { done() })
|
||||
// test('dial a conn on a protocol on top of a upgrade', function (done) { done() })
|
||||
})
|
||||
|
||||
/* TODO
|
||||
experiment('udp', function () {
|
||||
test('add the transport', function (done) { done() })
|
||||
test('dial a conn', function (done) { done() })
|
||||
test('dial a conn on a protocol', function (done) { done() })
|
||||
test('add an upgrade', function (done) { done() })
|
||||
test('dial a conn on top of a upgrade', function (done) { done() })
|
||||
test('dial a conn on a protocol on top of a upgrade', function (done) { done() })
|
||||
}) */
|
||||
|
||||
/* TODO
|
||||
experiment('udt', function () {
|
||||
test('add the transport', function (done) { done() })
|
||||
test('dial a conn', function (done) { done() })
|
||||
test('dial a conn on a protocol', function (done) { done() })
|
||||
test('add an upgrade', function (done) { done() })
|
||||
test('dial a conn on top of a upgrade', function (done) { done() })
|
||||
test('dial a conn on a protocol on top of a upgrade', function (done) { done() })
|
||||
}) */
|
||||
|
||||
/* TODO
|
||||
experiment('utp', function () {
|
||||
test('add the transport', function (done) { done() })
|
||||
test('dial a conn', function (done) { done() })
|
||||
test('dial a conn on a protocol', function (done) { done() })
|
||||
test('add an upgrade', function (done) { done() })
|
||||
test('dial a conn on top of a upgrade', function (done) { done() })
|
||||
test('dial a conn on a protocol on top of a upgrade', function (done) { done() })
|
||||
}) */
|
||||
})
|
||||
|
||||
experiment('BASICS', function () {
|
||||
experiment('Swarm', function () {
|
||||
test('enforces instantiation with new', function (done) {
|
||||
expect(function () {
|
||||
Swarm()
|
||||
}).to.throw('Swarm must be called with new')
|
||||
experiment('With a SPDY Stream Muxer', function () {
|
||||
experiment('tcp', function () {
|
||||
test('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()
|
||||
})
|
||||
|
||||
test('parses $IPFS_SWARM_PORT', function (done) {
|
||||
process.env.IPFS_SWARM_PORT = 1111
|
||||
var swarm = new Swarm()
|
||||
expect(swarm.port).to.be.equal(1111)
|
||||
process.env.IPFS_SWARM_PORT = undefined
|
||||
test('dial a conn on a protocol', function (done) {
|
||||
var mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010')
|
||||
var p1 = new Peer(Id.create(), [])
|
||||
var sw1 = new Swarm(p1)
|
||||
sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, ready)
|
||||
sw1.addStreamMuxer('spdy', Spdy, {})
|
||||
|
||||
var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
|
||||
var p2 = new Peer(Id.create(), [])
|
||||
var sw2 = new Swarm(p2)
|
||||
sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, ready)
|
||||
sw2.addStreamMuxer('spdy', Spdy, {})
|
||||
|
||||
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)
|
||||
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()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
experiment('Swarm.listen', function (done) {
|
||||
test('handles missing port', function (done) {
|
||||
var swarm = new Swarm()
|
||||
swarm.listen(done)
|
||||
})
|
||||
var count = 0
|
||||
|
||||
function ready () {
|
||||
count++
|
||||
if (count < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
})
|
||||
}
|
||||
})
|
||||
test('dial two conns (transport reuse)', function (done) {
|
||||
var mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010')
|
||||
var p1 = new Peer(Id.create(), [])
|
||||
var sw1 = new Swarm(p1)
|
||||
sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, ready)
|
||||
sw1.addStreamMuxer('spdy', Spdy, {})
|
||||
|
||||
var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
|
||||
var p2 = new Peer(Id.create(), [])
|
||||
var sw2 = new Swarm(p2)
|
||||
sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, ready)
|
||||
sw2.addStreamMuxer('spdy', Spdy, {})
|
||||
|
||||
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)
|
||||
conn.end()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
test('handles passed in port', function (done) {
|
||||
var swarm = new Swarm()
|
||||
swarm.listen(1234)
|
||||
expect(swarm.port).to.be.equal(1234)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
experiment('Swarm.registerHandler', function () {
|
||||
test('throws when registering a protcol handler twice', function (done) {
|
||||
var swarm = new Swarm()
|
||||
swarm.registerHandler('/sparkles/1.1.1', function () {})
|
||||
swarm.registerHandler('/sparkles/1.1.1', function (err) {
|
||||
expect(err).to.be.an.instanceOf(Error)
|
||||
expect(err.message).to.be.equal('Handle for protocol already exists')
|
||||
done()
|
||||
})
|
||||
})
|
||||
var count = 0
|
||||
|
||||
function ready () {
|
||||
count++
|
||||
if (count < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
experiment('Swarm.closeConns', function () {
|
||||
test('calls end on all connections', function (done) {
|
||||
swarmA.openConnection(peerB, function () {
|
||||
var key = Object.keys(swarmA.connections)[0]
|
||||
sinon.spy(swarmA.connections[key].conn, 'end')
|
||||
swarmA.closeConns(function () {
|
||||
expect(swarmA.connections[key].conn.end.called).to.be.equal(true)
|
||||
done()
|
||||
conn.on('data', function () {})
|
||||
expect(err).to.equal(null)
|
||||
expect(Object.keys(sw1.conns).length).to.equal(0)
|
||||
conn.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
experiment('BASE', function () {
|
||||
test('Open a stream', function (done) {
|
||||
var protocol = '/sparkles/3.3.3'
|
||||
var c = new Counter(2, done)
|
||||
|
||||
swarmB.registerHandler(protocol, function (stream) {
|
||||
c.hit()
|
||||
}
|
||||
})
|
||||
|
||||
swarmA.openStream(peerB, protocol, function (err, stream) {
|
||||
expect(err).to.not.be.instanceof(Error)
|
||||
c.hit()
|
||||
})
|
||||
})
|
||||
test('identify', function (done) {
|
||||
var mh1 = multiaddr('/ip4/127.0.0.1/tcp/8010')
|
||||
var p1 = new Peer(Id.create(), [])
|
||||
var sw1 = new Swarm(p1)
|
||||
sw1.addTransport('tcp', tcp, { multiaddr: mh1 }, {}, {port: 8010}, ready)
|
||||
sw1.addStreamMuxer('spdy', Spdy, {})
|
||||
sw1.enableIdentify()
|
||||
|
||||
test('Reuse connection (from dialer)', function (done) {
|
||||
var protocol = '/sparkles/3.3.3'
|
||||
var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
|
||||
var p2 = new Peer(Id.create(), [])
|
||||
var sw2 = new Swarm(p2)
|
||||
sw2.addTransport('tcp', tcp, { multiaddr: mh2 }, {}, {port: 8020}, ready)
|
||||
sw2.addStreamMuxer('spdy', Spdy, {})
|
||||
sw2.enableIdentify()
|
||||
|
||||
swarmB.registerHandler(protocol, function (stream) {})
|
||||
sw2.handleProtocol('/sparkles/1.0.0', function (conn) {
|
||||
// formallity so that the conn starts flowing
|
||||
conn.on('data', function (chunk) {})
|
||||
|
||||
swarmA.openStream(peerB, protocol, function (err, stream) {
|
||||
expect(err).to.not.be.instanceof(Error)
|
||||
conn.end()
|
||||
conn.on('end', function () {
|
||||
expect(Object.keys(sw1.muxedConns).length).to.equal(1)
|
||||
|
||||
swarmA.openStream(peerB, protocol, function (err, stream) {
|
||||
expect(err).to.not.be.instanceof(Error)
|
||||
expect(swarmA.connections.length === 1)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
test('Check for lastSeen', function (done) {
|
||||
var protocol = '/sparkles/3.3.3'
|
||||
var cleaningCounter = 0
|
||||
sw1.closeConns(cleaningUp)
|
||||
sw2.closeConns(cleaningUp)
|
||||
|
||||
swarmB.registerHandler(protocol, function (stream) {})
|
||||
sw1.closeListener('tcp', cleaningUp)
|
||||
sw2.closeListener('tcp', cleaningUp)
|
||||
|
||||
swarmA.openStream(peerB, protocol, function (err, stream) {
|
||||
expect(err).to.not.be.instanceof(Error)
|
||||
expect(peerB.lastSeen).to.be.instanceof(Date)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
experiment('IDENTIFY', function () {
|
||||
test('Attach Identify, open a stream, see a peer update', function (done) {
|
||||
swarmA.on('error', function (err) {
|
||||
console.log('A - ', err)
|
||||
})
|
||||
|
||||
swarmB.on('error', function (err) {
|
||||
console.log('B - ', err)
|
||||
})
|
||||
|
||||
var protocol = '/sparkles/3.3.3'
|
||||
|
||||
var identifyA = new Identify(swarmA, peerA)
|
||||
var identifyB = new Identify(swarmB, peerB)
|
||||
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 () {
|
||||
swarmB.registerHandler(protocol, function (stream) {})
|
||||
|
||||
swarmA.openStream(peerB, protocol, function (err, stream) {
|
||||
expect(err).to.not.be.instanceof(Error)
|
||||
})
|
||||
|
||||
identifyB.on('peer-update', function (answer) {
|
||||
expect(Object.keys(sw2.muxedConns).length).to.equal(1)
|
||||
done()
|
||||
})
|
||||
identifyA.on('peer-update', function (answer) {})
|
||||
}, 500)
|
||||
})
|
||||
|
||||
test('Attach Identify, open a stream, reuse stream', function (done) {
|
||||
var protocol = '/sparkles/3.3.3'
|
||||
|
||||
var identifyA = new Identify(swarmA, peerA)
|
||||
var identifyB = new Identify(swarmB, peerB)
|
||||
|
||||
swarmA.registerHandler(protocol, function (stream) {})
|
||||
swarmB.registerHandler(protocol, function (stream) {})
|
||||
|
||||
swarmA.openStream(peerB, protocol, function (err, stream) {
|
||||
expect(err).to.not.be.instanceof(Error)
|
||||
})
|
||||
|
||||
identifyB.on('peer-update', function (answer) {
|
||||
expect(Object.keys(swarmB.connections).length).to.equal(1)
|
||||
swarmB.openStream(peerA, protocol, function (err, stream) {
|
||||
expect(err).to.not.be.instanceof(Error)
|
||||
expect(Object.keys(swarmB.connections).length).to.equal(1)
|
||||
done()
|
||||
}
|
||||
})
|
||||
})
|
||||
identifyA.on('peer-update', function (answer) {})
|
||||
})
|
||||
|
||||
test('Attach Identify, reuse peer', function (done) {
|
||||
var protocol = '/sparkles/3.3.3'
|
||||
var count = 0
|
||||
|
||||
var identifyA = new Identify(swarmA, peerA)
|
||||
var identifyB = new Identify(swarmB, peerB) // eslint-disable-line no-unused-vars
|
||||
|
||||
swarmA.registerHandler(protocol, function (stream) {})
|
||||
swarmB.registerHandler(protocol, function (stream) {})
|
||||
|
||||
var restartA = function (cb) {
|
||||
swarmA.openStream(peerB, protocol, function (err, stream) {
|
||||
expect(err).to.not.be.instanceof(Error)
|
||||
|
||||
stream.end(cb)
|
||||
})
|
||||
function ready () {
|
||||
count++
|
||||
if (count < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
restartA(function () {
|
||||
identifyA.once('peer-update', function () {
|
||||
expect(peerA.previousObservedAddrs.length).to.be.equal(1)
|
||||
|
||||
var c = new Counter(2, done)
|
||||
|
||||
swarmA.closeConns(c.hit.bind(c))
|
||||
swarmB.closeConns(c.hit.bind(c))
|
||||
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()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
experiment('HARDNESS', function () {})
|
||||
|
||||
function Counter (target, callback) {
|
||||
var c = 0
|
||||
this.hit = count
|
||||
|
||||
function count () {
|
||||
c += 1
|
||||
if (c === target) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// function checkErr (err) {
|
||||
// console.log('err')
|
||||
// expect(err).to.be.instanceof(Error)
|
||||
// }
|
||||
|
Loading…
x
Reference in New Issue
Block a user