This repository has been archived by the owner on Aug 23, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 37
Revisit Swarm - multitransport + upgrades - https://github.com/diasdavid/node-ipfs-swarm/issues/8 #10
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
544e4a4
update README with new candidate interface
daviddias 1833ded
making progress
daviddias 73a6a4f
adding transports works
daviddias e1df0b9
add transport and close listener test
daviddias 5e4cca5
dial a conn + test
daviddias 8e8d8e9
dial on a protocol + test
daviddias 59b00f6
use warmed up connection + test
daviddias 416e107
quick fix for travis
daviddias 0040be7
add spdy + test
daviddias 168d01b
stream muxer for connection reuse test
daviddias 0bcbe63
rm old code
daviddias 2000827
add identify
daviddias fb37b4d
clear unused console logs
daviddias e6bcde4
change cov
daviddias 92b499d
fix readme typos and missing links
daviddias 5fe9444
rm old test file
daviddias 5b7a605
comment undone tests
daviddias 1ba8e80
rm laf
daviddias File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,69 @@ | ||
ipfs-swarm Node.js implementation | ||
libp2p-swarm Node.js implementation | ||
================================= | ||
|
||
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) [![Build Status](https://img.shields.io/travis/diasdavid/node-ipfs-swarm/master.svg?style=flat-square)](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 | ||
``` | ||
|
||
### Close the listener/socket and every open stream that was multiplexed on it | ||
```JavaScript | ||
var Swarm = require('libp2p-swarm') | ||
|
||
```javascript | ||
s.closeListener() | ||
var sw = new Swarm(peerInfoSelf) | ||
``` | ||
|
||
### Register a protocol to be handled by an incoming stream | ||
|
||
```javascript | ||
s.registerHandler('/name/protocol/you/want/version', function (stream) {}) | ||
``` | ||
peerInfoSelf is a [PeerInfo](https://github.com/diasdavid/node-peer-info) object that represents the peer creating this swarm instance. | ||
|
||
### Open a new connection | ||
### Support a transport | ||
|
||
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. | ||
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. | ||
|
||
``` | ||
s.openConnection(peerConnection, function (err) {}) | ||
```JavaScript | ||
sw.addTransport(transport, [options, dialOptions, listenOptions]) | ||
``` | ||
|
||
### Add a connection upgrade | ||
|
||
### Dial a new 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]) | ||
``` | ||
s.openStream(peerInfo, protocol, function (err, stream) {}) | ||
``` | ||
|
||
peerInfo must be a [`ipfs-peer`](https://www.npmjs.com/package/ipfs-peer) object, contaning both peer-id and multiaddrs. | ||
|
||
## Events emitted | ||
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. | ||
|
||
```JavaScript | ||
sw.addStreamMuxer(streamMuxer, [options]) | ||
``` | ||
.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: | ||
### Dial to another peer | ||
|
||
```JavaScript | ||
sw.dial(PeerInfo, options, protocol) | ||
sw.dial(PeerInfo, options) | ||
``` | ||
var Identify = require('ipfs-swarm/identify') | ||
|
||
var i = new Identify(swarmInstance, peerSelf) | ||
``` | ||
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. | ||
|
||
`swarmInstance` must be an Instance of swarm and `peerSelf` must be a instance of `ipfs-peer` that represents the peer that instantiated this Identify | ||
### Accept requests on a specific protocol | ||
|
||
Identify emits a `peer-update` event each time it receives information from another peer. | ||
```JavaScript | ||
sw.handleProtocol(protocol, handlerFunction) | ||
``` |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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') | ||
}) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,93 +4,110 @@ | |
*/ | ||
|
||
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() | ||
function identify (muxedConns, peerInfoSelf, socket, conn, muxer) { | ||
var msi = new Interactive() | ||
msi.handle(conn, function () { | ||
msi.select(protoId, function (err, ds) { | ||
if (err) { | ||
return console.log(err) // TODO Treat error | ||
} | ||
|
||
ps.on('identify', function (msg) { | ||
updateSelf(peerSelf, msg.observedAddr) | ||
var ps = createProtoStream() | ||
|
||
var peerId = Id.createFromPubKey(msg.publicKey) | ||
ps.on('identify', function (msg) { | ||
var peerId = Id.createFromPubKey(msg.publicKey) | ||
|
||
updateSelf(peerInfoSelf, msg.observedAddr) | ||
|
||
muxedConns[peerId.toB58String()] = { | ||
muxer: muxer, | ||
socket: socket | ||
} | ||
|
||
// 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 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}), | ||
publicKey: peerInfoSelf.id.pubKey, | ||
listenAddrs: peerInfoSelf.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.pipe(ds).pipe(ps) | ||
ps.finalize() | ||
}) | ||
ps.pipe(stream).pipe(ps) | ||
}) | ||
} | ||
|
||
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) | ||
|
||
swarm.on('connection-unknown', function (conn, socket) { | ||
conn.dialStream(function (err, stream) { | ||
if (err) { return console.log(err) } | ||
var msi = new Interactive() | ||
msi.handle(stream, function () { | ||
msi.select('/ipfs/identify/1.0.0', function (err, ds) { | ||
if (err) { return console.log(err) } | ||
|
||
var ps = self.createProtoStream() | ||
|
||
ps.on('identify', function (msg) { | ||
var peerId = Id.createFromPubKey(msg.publicKey) | ||
|
||
updateSelf(peerSelf, msg.observedAddr) | ||
|
||
swarm.connections[peerId.toB58String()] = { | ||
conn: conn, | ||
socket: socket | ||
} | ||
|
||
self.emit('peer-update', { | ||
peerId: peerId, | ||
listenAddrs: msg.listenAddrs.map(function (mhb) {return multiaddr(mhb)}) | ||
}) | ||
}) | ||
|
||
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 | ||
}) | ||
|
||
ps.pipe(ds).pipe(ps) | ||
ps.finalize() | ||
}) | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate comment. Delete lines 97-106. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll keep these though as it is a good note for me to remember. But you are right, it is a dup comment, thank you for catching that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still have a dupe comment? Other than that, good to merge. |
||
// 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) { | ||
|
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs
module.exports
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check line 14
exports = module.exports = identify
that is typically the pattern I go for :)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Word.