diff --git a/README.md b/README.md
index 45a97cfbdc..57ca948416 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ libp2p is the product of a long and arduous quest to understand the evolution of
We are in the process of writting better documentation, blog posts, tutorials and a formal specification. Today you can find:
-- [libp2p.io - The libp2p Website (WIP)](https://github.com/libp2p/website)
+- [libp2p.io](https://libp2p.io)
- [Specification (WIP)](https://github.com/libp2p/specs)
- Talks
- [`libp2p <3 ethereum` at DEVCON2](https://ethereumfoundation.org/devcon/?session=libp2p) [video](https://www.youtube.com/watch?v=HxueJbeMVG4) [slides](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p-HEART-devp2p-IPFS-PLUS-Ethereum-networking.pdf) [demo-1](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo1-1.mp4) [demo-2](https://ethereumfoundation.org/devcon/wp-content/uploads/2016/10/libp2p_demo2-1.mp4)
@@ -41,8 +41,8 @@ To sum up, libp2p is a "network stack" -- a protocol suite -- that cleanly separ
With its modular nature, libp2p can be found being used in different projects with different sets of features, while preserving the same top level API. `js-libp2p` is only a skeleton and should not be installed directly, if you are looking for a prebundled libp2p stack, please check:
-- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-libp2p-ipfs-nodejs) - The libp2p build used by js-ipfs when run in Node.js
-- [libp2p-ipfs-browser](https://github.com/ipfs/js-libp2p-ipfs-browser) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC)
+- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-nodejs.js) - The libp2p build used by js-ipfs when run in Node.js
+- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-browser.js) - The libp2p build used by js-ipfs when run in a Browser (that supports WebRTC)
If you have developed a libp2p bundle, please consider submitting it to this list so that it can be found easily by the users of libp2p.
diff --git a/examples/README.md b/examples/README.md
index fc8ae2e299..d0d3503ab4 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,6 +1,13 @@
-### Examples list
+# `js-libp2p` Examples and Tutorials
-Here are some examples built with libp2p bundles.
+In this folder, you can find a variety of examples to help you get started in using js-libp2p, in Node.js and in the Browser. Every example as a specific purpose and some of each incorporate a full tutorial that you can follow through, helping you expand your knowledge about libp2p and p2p networks in general.
-- https://github.com/ipfs/js-libp2p-ipfs/tree/master/examples/echo
-- https://github.com/ipfs/js-libp2p-ipfs/tree/master/examples/chat
+Let us know if you find any issue or if you want to contribute and add a new tutorial, feel welcome to submit a PR, thank you!
+
+## Examples
+
+- [In Node.js](./nodejs)
+ - [echo](./nodejs/echo)
+ - [chat](./nodejs/chat)
+- [In the browser](./browser)
+ - [mapper](./browser/mapper)
diff --git a/examples/archived/README.md b/examples/archived/README.md
deleted file mode 100644
index 293f632229..0000000000
--- a/examples/archived/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Using libp2p-swarm
-==================
-
-
diff --git a/examples/archived/node-1.js b/examples/archived/node-1.js
deleted file mode 100644
index f6c35228b6..0000000000
--- a/examples/archived/node-1.js
+++ /dev/null
@@ -1,39 +0,0 @@
-'use strict'
-
-var Swarm = require('libp2p-swarm')
-var tcp = require('libp2p-tcp')
-var multiaddr = require('multiaddr')
-var Id = require('peer-id')
-var Spdy = require('libp2p-spdy')
-var Libp2p = require('../../src')
-var Peer = require('peer-info')
-
-// set up
-
-var mh = multiaddr('/ip4/127.0.0.1/tcp/8010')
-var p = new Peer(Id.create(), [])
-var sw = new Swarm(p)
-
-// create a libp2p node
-
-var node = new Libp2p(sw)
-
-node.swarm.addTransport('tcp', tcp, {multiaddr: mh}, {}, {port: 8010}, function () {
- // Ready to receive incoming connections
-
- sw.addStreamMuxer('spdy', Spdy, {})
-
- // dial to another node
-
- var mh2 = multiaddr('/ip4/127.0.0.1/tcp/8020')
- var p2 = new Peer(Id.create(), [mh2])
-
- node.swarm.dial(p2, {}, '/sparkles/1.0.0', function (err, conn) {
- if (err) {
- return console.error(err)
- }
-
- console.log('-> connection is ready')
- process.stdin.pipe(conn).pipe(process.stdout)
- })
-})
diff --git a/examples/archived/node-2.js b/examples/archived/node-2.js
deleted file mode 100644
index 4e25ad9fab..0000000000
--- a/examples/archived/node-2.js
+++ /dev/null
@@ -1,31 +0,0 @@
-'use strict'
-
-var Swarm = require('libp2p-swarm')
-var tcp = require('libp2p-tcp')
-var multiaddr = require('multiaddr')
-var Id = require('peer-id')
-var Spdy = require('libp2p-spdy')
-var Libp2p = require('../../src')
-var Peer = require('peer-info')
-
-// set up
-
-var mh = multiaddr('/ip4/127.0.0.1/tcp/8020')
-var p = new Peer(Id.create(), [])
-var sw = new Swarm(p)
-
-sw.addTransport('tcp', tcp, {multiaddr: mh}, {}, {port: 8020}, function () {
- // Ready to receive incoming connections
-
- sw.addStreamMuxer('spdy', Spdy, {})
-
- // create a libp2p node
-
- var node = new Libp2p(sw)
-
- // handle/mount a protocol
-
- node.swarm.handleProtocol('/sparkles/1.0.0', function (conn) {
- process.stdin.pipe(conn).pipe(process.stdout)
- })
-})
diff --git a/examples/browser-chat/.gitkeep b/examples/browser-chat/.gitkeep
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/browser-echo/README.md b/examples/browser-echo/README.md
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/browser/mapper/.gitignore b/examples/browser/mapper/.gitignore
new file mode 100644
index 0000000000..4187d67743
--- /dev/null
+++ b/examples/browser/mapper/.gitignore
@@ -0,0 +1 @@
+bundle.js
\ No newline at end of file
diff --git a/examples/browser/mapper/package.json b/examples/browser/mapper/package.json
new file mode 100644
index 0000000000..b9c2724b42
--- /dev/null
+++ b/examples/browser/mapper/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "mapper",
+ "version": "0.0.0",
+ "description": "",
+ "main": "src/index.js",
+ "scripts": {
+ "bundle": "browserify src/index.js --require browserify-zlib-next:zlib > public/bundle.js",
+ "serve": "static public -p 9090 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'",
+ "mon": "nodemon --exec \"npm run start\" --ignore public/bundle.js",
+ "start": "npm run bundle && npm run serve"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "browserify": "^14.0.0",
+ "browserify-optional": "^1.0.0",
+ "browserify-zlib-next": "^1.0.1",
+ "concat-stream": "^1.6.0",
+ "detect-dom-ready": "^1.0.2",
+ "node-static": "^0.7.9",
+ "nodemon": "^1.11.0"
+ },
+ "dependencies": {}
+}
diff --git a/examples/browser/mapper/public/index.html b/examples/browser/mapper/public/index.html
new file mode 100644
index 0000000000..7d77a39c12
--- /dev/null
+++ b/examples/browser/mapper/public/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+ p2p mapper
+
+
+ p2p mapper
+
+
+
+
+
+
diff --git a/examples/browser/mapper/src/create-node.js b/examples/browser/mapper/src/create-node.js
new file mode 100644
index 0000000000..6e364560f5
--- /dev/null
+++ b/examples/browser/mapper/src/create-node.js
@@ -0,0 +1,24 @@
+'use strict'
+
+const PeerInfo = require('peer-info')
+const Node = require('../../../../test/browser-bundle/browser-bundle.js')
+
+function createNode (callback) {
+ PeerInfo.create((err, peerInfo) => {
+ if (err) {
+ return callback(err)
+ }
+
+ const peerIdStr = peerInfo.id.toB58String()
+ const ma = `/libp2p-webrtc-star/dns4/star-signal.cloud.ipfs.team/wss/ipfs/${peerIdStr}`
+
+ peerInfo.multiaddrs.add(ma)
+
+ const node = new Node(peerInfo, undefined, { webRTCStar: true })
+
+ node.idStr = peerIdStr
+ callback(null, node)
+ })
+}
+
+module.exports = createNode
diff --git a/examples/browser/mapper/src/index.js b/examples/browser/mapper/src/index.js
new file mode 100644
index 0000000000..7100454b8a
--- /dev/null
+++ b/examples/browser/mapper/src/index.js
@@ -0,0 +1,58 @@
+'use strict'
+
+const domReady = require('detect-dom-ready')
+const createNode = require('./create-node')
+
+domReady(() => {
+ const myPeerDiv = document.getElementById('my-peer')
+ const swarmDiv = document.getElementById('swarm')
+
+ createNode((err, node) => {
+ if (err) {
+ return console.log('Could not create the Node, check if your browser has WebRTC Support', err)
+ }
+
+ node.on('peer:discovery', (peerInfo) => {
+ console.log('Discovered a peer')
+ const idStr = peerInfo.id.toB58String()
+ console.log('Discovered: ' + idStr)
+
+ node.dial(peerInfo, (err, conn) => {
+ if (err) { return console.log('Failed to dial:', idStr) }
+ })
+ })
+
+ node.on('peer:connect', (peerInfo) => {
+ const idStr = peerInfo.id.toB58String()
+ console.log('Got connection to: ' + idStr)
+ const connDiv = document.createElement('div')
+ connDiv.innerHTML = 'Connected to: ' + idStr
+ connDiv.id = idStr
+ swarmDiv.append(connDiv)
+ })
+
+ node.on('peer:disconnect', (peerInfo) => {
+ const idStr = peerInfo.id.toB58String()
+ console.log('Lost connection to: ' + idStr)
+ document.getElementById(idStr).remove()
+ })
+
+ node.start((err) => {
+ if (err) {
+ return console.log('WebRTC not supported')
+ }
+
+ const idStr = node.peerInfo.id.toB58String()
+
+ const idDiv = document
+ .createTextNode('Node is ready. ID: ' + idStr)
+
+ myPeerDiv.append(idDiv)
+
+ console.log('Node is listening o/')
+
+ // NOTE: to stop the node
+ // node.stop((err) => {})
+ })
+ })
+})
diff --git a/examples/node-browser-chat/README.md b/examples/node-browser-chat/README.md
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/node-browser-echo/README.md b/examples/node-browser-echo/README.md
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/node-chat/.gitkeep b/examples/node-chat/.gitkeep
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/node-echo/README.md b/examples/node-echo/README.md
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/nodejs/chat/dialer.js b/examples/nodejs/chat/dialer.js
new file mode 100644
index 0000000000..88ae5cd4c6
--- /dev/null
+++ b/examples/nodejs/chat/dialer.js
@@ -0,0 +1,78 @@
+'use strict'
+/* eslint-disable no-console */
+
+const PeerId = require('peer-id')
+const PeerInfo = require('peer-info')
+const Node = require('../../../../test/nodejs-bundle/nodejs-bundle.js')
+const multiaddr = require('multiaddr')
+const pull = require('pull-stream')
+const async = require('async')
+const Pushable = require('pull-pushable')
+const p = Pushable()
+let idListener
+
+async.parallel([
+ (callback) => {
+ PeerId.createFromJSON(require('./peer-id-dialer'), (err, idDialer) => {
+ if (err) {
+ throw err
+ }
+ callback(null, idDialer)
+ })
+ },
+ (callback) => {
+ PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
+ if (err) {
+ throw err
+ }
+ callback(null, idListener)
+ })
+ }
+], (err, ids) => {
+ if (err) throw err
+ const peerDialer = new PeerInfo(ids[0])
+ peerDialer.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/0'))
+ const nodeDialer = new Node(peerDialer)
+
+ const peerListener = new PeerInfo(ids[1])
+ idListener = ids[1]
+ peerListener.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/10333'))
+ nodeDialer.start((err) => {
+ if (err) {
+ throw err
+ }
+
+ console.log('Dialer ready, listening on:')
+
+ peerListener.multiaddrs.forEach((ma) => {
+ console.log(ma.toString() + '/ipfs/' + idListener.toB58String())
+ })
+
+ nodeDialer.dialByPeerInfo(peerListener, '/chat/1.0.0', (err, conn) => {
+ if (err) {
+ throw err
+ }
+ console.log('nodeA dialed to nodeB on protocol: /chat/1.0.0')
+ console.log('Type a message and see what happens')
+ // Write operation. Data sent as a buffer
+ pull(
+ p,
+ conn
+ )
+ // Sink, data converted from buffer to utf8 string
+ pull(
+ conn,
+ pull.map((data) => {
+ return data.toString('utf8').replace('\n', '')
+ }),
+ pull.drain(console.log)
+ )
+
+ process.stdin.setEncoding('utf8')
+ process.openStdin().on('data', (chunk) => {
+ var data = chunk.toString()
+ p.push(data)
+ })
+ })
+ })
+})
diff --git a/examples/nodejs/chat/listener.js b/examples/nodejs/chat/listener.js
new file mode 100644
index 0000000000..435ab8ff80
--- /dev/null
+++ b/examples/nodejs/chat/listener.js
@@ -0,0 +1,55 @@
+'use strict'
+/* eslint-disable no-console */
+
+const PeerId = require('peer-id')
+const PeerInfo = require('peer-info')
+const Node = require('../../../../test/nodejs-bundle/nodejs-bundle.js')
+const multiaddr = require('multiaddr')
+const pull = require('pull-stream')
+const Pushable = require('pull-pushable')
+const p = Pushable()
+
+PeerId.createFromJSON(require('./peer-id-listener'), (err, idListener) => {
+ if (err) {
+ throw err
+ }
+ const peerListener = new PeerInfo(idListener)
+ peerListener.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/10333'))
+ const nodeListener = new Node(peerListener)
+
+ nodeListener.start((err) => {
+ if (err) {
+ throw err
+ }
+
+ nodeListener.swarm.on('peer-mux-established', (peerInfo) => {
+ console.log(peerInfo.id.toB58String())
+ })
+
+ nodeListener.handle('/chat/1.0.0', (protocol, conn) => {
+ pull(
+ p,
+ conn
+ )
+
+ pull(
+ conn,
+ pull.map((data) => {
+ return data.toString('utf8').replace('\n', '')
+ }),
+ pull.drain(console.log)
+ )
+
+ process.stdin.setEncoding('utf8')
+ process.openStdin().on('data', (chunk) => {
+ var data = chunk.toString()
+ p.push(data)
+ })
+ })
+
+ console.log('Listener ready, listening on:')
+ peerListener.multiaddrs.forEach((ma) => {
+ console.log(ma.toString() + '/ipfs/' + idListener.toB58String())
+ })
+ })
+})
diff --git a/examples/nodejs/chat/peer-id-dialer.json b/examples/nodejs/chat/peer-id-dialer.json
new file mode 100644
index 0000000000..5716d74baf
--- /dev/null
+++ b/examples/nodejs/chat/peer-id-dialer.json
@@ -0,0 +1,5 @@
+{
+ "id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP",
+ "privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=",
+ "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE="
+}
diff --git a/examples/nodejs/chat/peer-id-listener.json b/examples/nodejs/chat/peer-id-listener.json
new file mode 100644
index 0000000000..7acb97c8c1
--- /dev/null
+++ b/examples/nodejs/chat/peer-id-listener.json
@@ -0,0 +1,5 @@
+{
+ "id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm",
+ "privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6",
+ "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE="
+}
diff --git a/examples/nodejs/echo/dialer.js b/examples/nodejs/echo/dialer.js
new file mode 100644
index 0000000000..c3093292f6
--- /dev/null
+++ b/examples/nodejs/echo/dialer.js
@@ -0,0 +1,57 @@
+'use strict'
+/* eslint-disable no-console */
+
+/*
+ * Dialer Node
+ */
+
+const PeerId = require('peer-id')
+const PeerInfo = require('peer-info')
+const Node = require('../../../../test/nodejs-bundle/nodejs-bundle.js')
+const multiaddr = require('multiaddr')
+const pull = require('pull-stream')
+const async = require('async')
+
+async.parallel([
+ (cb) => PeerId.createFromJSON(require('./id-d'), cb),
+ (cb) => PeerId.createFromJSON(require('./id-l'), cb)
+], (err, ids) => {
+ if (err) { throw err }
+
+ // Dialer
+ const dialerId = ids[0]
+ const dialerPeerInfo = new PeerInfo(dialerId)
+ dialerPeerInfo.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/0'))
+ const dialerNode = new Node(dialerPeerInfo)
+
+ // Peer to Dial
+ const listenerPeerInfo = new PeerInfo(ids[1])
+ const listenerId = ids[1]
+ const listenerMultiaddr = multiaddr('/ip4/127.0.0.1/tcp/10333/ipfs/' +
+ listenerId.toB58String())
+ listenerPeerInfo.multiaddr.add(listenerMultiaddr)
+
+ dialerNode.start((err) => {
+ if (err) { throw err }
+
+ console.log('Dialer ready, listening on:')
+ dialerPeerInfo.multiaddrs.forEach((ma) => console.log(ma.toString() +
+ '/ipfs/' + dialerId.toB58String()))
+
+ console.log('Dialing to peer:', listenerMultiaddr.toString())
+ dialerNode.dialByPeerInfo(listenerPeerInfo, '/echo/1.0.0', (err, conn) => {
+ if (err) { throw err }
+
+ console.log('nodeA dialed to nodeB on protocol: /echo/1.0.0')
+
+ pull(
+ pull.values(['hey']),
+ conn,
+ pull.collect((err, data) => {
+ if (err) { throw err }
+ console.log('received echo:', data.toString())
+ })
+ )
+ })
+ })
+})
diff --git a/examples/nodejs/echo/id-d.json b/examples/nodejs/echo/id-d.json
new file mode 100644
index 0000000000..5716d74baf
--- /dev/null
+++ b/examples/nodejs/echo/id-d.json
@@ -0,0 +1,5 @@
+{
+ "id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP",
+ "privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=",
+ "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE="
+}
diff --git a/examples/nodejs/echo/id-l.json b/examples/nodejs/echo/id-l.json
new file mode 100644
index 0000000000..7acb97c8c1
--- /dev/null
+++ b/examples/nodejs/echo/id-l.json
@@ -0,0 +1,5 @@
+{
+ "id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm",
+ "privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6",
+ "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE="
+}
diff --git a/examples/nodejs/echo/listener.js b/examples/nodejs/echo/listener.js
new file mode 100644
index 0000000000..812dfc43d3
--- /dev/null
+++ b/examples/nodejs/echo/listener.js
@@ -0,0 +1,46 @@
+'use strict'
+/* eslint-disable no-console */
+
+/*
+ * Listener Node
+ */
+
+const PeerId = require('peer-id')
+const PeerInfo = require('peer-info')
+const Node = require('../../../../test/nodejs-bundle/nodejs-bundle.js')
+const multiaddr = require('multiaddr')
+const pull = require('pull-stream')
+const series = require('async/series')
+
+let listenerId
+let listenerNode
+
+series([
+ (cb) => {
+ PeerId.createFromJSON(require('./id-l'), (err, id) => {
+ if (err) { return cb(err) }
+ listenerId = id
+ cb()
+ })
+ },
+ (cb) => {
+ const listenerPeerInfo = new PeerInfo(listenerId)
+ listenerPeerInfo.multiaddr.add(multiaddr('/ip4/0.0.0.0/tcp/10333'))
+ listenerNode = new Node(listenerPeerInfo)
+
+ listenerNode.swarm.on('peer-mux-established', (peerInfo) => {
+ console.log('received dial to me from:', peerInfo.id.toB58String())
+ })
+
+ listenerNode.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
+
+ listenerNode.start(cb)
+ }
+], (err) => {
+ if (err) { throw err }
+
+ console.log('Listener ready, listening on:')
+ listenerNode.peerInfo.multiaddrs.forEach((ma) => {
+ console.log(ma.toString() + '/ipfs/' + listenerId.toB58String())
+ })
+})
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000000..694c65b55c
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,55 @@
+'use strict'
+
+const gulp = require('gulp')
+const Node = require('./test/nodejs-bundle/nodejs-bundle.js')
+const PeerInfo = require('peer-info')
+const PeerId = require('peer-id')
+const pull = require('pull-stream')
+
+const sigServer = require('libp2p-webrtc-star/src/sig-server')
+let server
+
+let node
+const rawPeer = require('./test/browser-bundle/peer.json')
+
+gulp.task('libnode:start', (done) => {
+ let count = 0
+ const ready = () => ++count === 2 ? done() : null
+
+ sigServer.start({ port: 15555 }, (err, _server) => {
+ if (err) {
+ throw err
+ }
+ server = _server
+ ready()
+ })
+
+ PeerId.createFromJSON(rawPeer, (err, peerId) => {
+ if (err) {
+ return done(err)
+ }
+ const peer = new PeerInfo(peerId)
+
+ peer.multiaddrs.add('/ip4/127.0.0.1/tcp/9200/ws')
+
+ node = new Node(peer)
+ node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
+ node.start(() => ready())
+ })
+})
+
+gulp.task('libnode:stop', (done) => {
+ setTimeout(() => node.stop((err) => {
+ if (err) {
+ return done(err)
+ }
+ server.stop(done)
+ }), 2000)
+})
+
+gulp.task('test:browser:before', ['libnode:start'])
+gulp.task('test:node:before', ['libnode:start'])
+gulp.task('test:browser:after', ['libnode:stop'])
+gulp.task('test:node:after', ['libnode:stop'])
+
+require('aegir/gulp')(gulp)
diff --git a/package.json b/package.json
index d8372ce1f5..f847287de1 100644
--- a/package.json
+++ b/package.json
@@ -4,11 +4,14 @@
"description": "JavaScript Skeleton for libp2p bundles",
"main": "src/index.js",
"scripts": {
- "test": "aegir-test node",
+ "test": "gulp test",
+ "test:node": "gulp test:node",
+ "test:browser": "gulp test:browser --dom",
+ "release": "gulp release --dom",
+ "release-minor": "gulp release --type minor --dom",
+ "release-major": "gulp release --type major --dom",
+ "build": "gulp build",
"lint": "aegir-lint",
- "release": "aegir-release node",
- "release-minor": "aegir-release --type minor",
- "release-major": "aegir-release --type major",
"coverage": "aegir-coverage",
"coverage-publish": "aegir-coverage publish"
},
@@ -34,13 +37,32 @@
},
"homepage": "https://github.com/libp2p/js-libp2p",
"devDependencies": {
- "aegir": "^11.0.1",
- "chai": "^3.5.0",
- "dirty-chai": "^1.2.2",
- "pre-commit": "^1.2.2"
+ "aegir": "^11.0.2",
+ "chai": "^4.0.2",
+ "cids": "^0.5.0",
+ "dirty-chai": "^2.0.0",
+ "electron-webrtc": "^0.3.0",
+ "libp2p-kad-dht": "^0.1.0",
+ "libp2p-mdns": "^0.7.0",
+ "libp2p-multiplex": "^0.4.3",
+ "libp2p-railing": "^0.5.1",
+ "libp2p-secio": "^0.6.8",
+ "libp2p-spdy": "^0.10.6",
+ "libp2p-swarm": "^0.29.1",
+ "libp2p-tcp": "^0.10.1",
+ "libp2p-webrtc-star": "^0.11.0",
+ "libp2p-websockets": "^0.10.0",
+ "lodash.times": "^4.3.2",
+ "mafmt": "^2.1.8",
+ "pre-commit": "^1.2.2",
+ "pull-goodbye": "0.0.2",
+ "pull-serializer": "^0.3.2",
+ "pull-stream": "^3.6.0",
+ "safe-buffer": "^5.1.1",
+ "wrtc": "0.0.62"
},
"dependencies": {
- "async": "^2.4.1",
+ "async": "^2.5.0",
"libp2p-ping": "~0.3.2",
"libp2p-swarm": "~0.29.1",
"mafmt": "^2.1.8",
@@ -56,4 +78,4 @@
"greenkeeperio-bot ",
"mayerwin "
]
-}
\ No newline at end of file
+}
diff --git a/test/index.spec.js b/test/base.js
similarity index 100%
rename from test/index.spec.js
rename to test/base.js
diff --git a/test/browser-bundle/browser-bundle.js b/test/browser-bundle/browser-bundle.js
new file mode 100644
index 0000000000..9d80ad1948
--- /dev/null
+++ b/test/browser-bundle/browser-bundle.js
@@ -0,0 +1,67 @@
+'use strict'
+
+const WS = require('libp2p-websockets')
+const WebRTCStar = require('libp2p-webrtc-star')
+const spdy = require('libp2p-spdy')
+const multiplex = require('libp2p-multiplex')
+const secio = require('libp2p-secio')
+const Railing = require('libp2p-railing')
+const libp2p = require('../..')
+
+function mapMuxers (list) {
+ return list.map((pref) => {
+ if (typeof pref !== 'string') {
+ return pref
+ }
+ switch (pref.trim().toLowerCase()) {
+ case 'spdy':
+ return spdy
+ case 'multiplex':
+ return multiplex
+ default:
+ throw new Error(pref + ' muxer not available')
+ }
+ })
+}
+
+function getMuxers (options) {
+ if (options) {
+ return mapMuxers(options)
+ } else {
+ return [multiplex, spdy]
+ }
+}
+
+class Node extends libp2p {
+ constructor (peerInfo, peerBook, options) {
+ options = options || {}
+ const webRTCStar = new WebRTCStar()
+
+ const modules = {
+ transport: [
+ new WS(),
+ webRTCStar
+ ],
+ connection: {
+ muxer: getMuxers(options.muxer),
+ crypto: [
+ secio
+ ]
+ },
+ discovery: []
+ }
+
+ if (options.webRTCStar) {
+ modules.discovery.push(webRTCStar.discovery)
+ }
+
+ if (options.bootstrap) {
+ const r = new Railing(options.bootstrap)
+ modules.discovery.push(r)
+ }
+
+ super(modules, peerInfo, peerBook, options)
+ }
+}
+
+module.exports = Node
diff --git a/test/browser-bundle/peer.json b/test/browser-bundle/peer.json
new file mode 100644
index 0000000000..ba789ca32d
--- /dev/null
+++ b/test/browser-bundle/peer.json
@@ -0,0 +1,5 @@
+{
+ "id": "Qmex1SSsueWFsUfjdkugJ5zhcnjddAt8TxcnDLUXKD9Sx7",
+ "privKey": "CAASqAkwggSkAgEAAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAECggEAdBUzV/GaQ0nmoQrWvOnUxmFIho7kCjkh1NwnNVPNc+Msa1r7pcI9wJNPwap8j1w4L/cZuYhOJgcg+o2mWFiuULKZ4F9Ro/M89gZ038457g2/2pPu43c/Xoi/2YcAHXg0Gr+OCe2zCIyITBWKAFqyAzL6DubAxrJW2Ezj1LrZ+EZgMyzbh/go/eEGSJaaGkINeAkY144DqDWWWvzyhKhryipsGkZGEkVy9xJgMEI3ipVvuPez2XAvoyyeuinBBLe+Z2vY5G50XXzbIMhIQGLncHf9MwTv6wt1ilyOSLOXK0BoQbB76J3R3is5dSULXXP9r8VocjLBEkmBuf4FXAKzoQKBgQDNNS4F1XE1gxD8LPkL+aB/hi6eVHVPhr+w0I/9ATikcLGeUfBM2Gd6cZRPFtNVrv1p6ZF1D1UyGDknGbDBSQd9wLUgb0fDoo3jKYMGWq6G+VvaP5rzWQeBV8YV2EhSmUk1i6kiYe2ZE8WyrPie7iwpQIY60e2A8Ly0GKZiBZUcHQKBgQC9YDAVsGnEHFVFkTDpvw5HwEzCgTb2A3NgkGY3rTYZ7L6AFjqCYmUwFB8Fmbyc4kdFWNh8wfmq5Qrvl49NtaeukiqWKUUlB8uPdztB1P0IahA2ks0owStZlRifmwfgYyMd4xE17lhaOgQQJZZPxmP0F6mdOvb3YJafNURCdMS51wKBgEvvIM+h0tmFXXSjQ6kNvzlRMtD92ccKysYn9xAdMpOO6/r0wSH+dhQWEVZO0PcE4NsfReb2PIVj90ojtIdhebcr5xpQc1LORQjJJKXmSmzBux6AqNrhl+hhzXfp56FA/Zkly/lgGWaqrV5XqUxOP+Mn8EO1yNgMvRc7g94DyNB1AoGBAKLBuXHalXwDsdHBUB2Eo3xNLGt6bEcRfia+0+sEBdxQGQWylQScFkU09dh1YaIf44sZKa5HdBFJGpYCVxo9hmjFnK5Dt/Z0daHOonIY4INLzLVqg8KECoLKXkhGEIXsDjFQhukn+G1LMVTDSSU055DQiWjlVX4UWD9qo0jOXIkvAoGBAMP50p2X6PsWWZUuuR7i1JOJHRyQZPWdHh9p8SSLnCtEpHYZfJr4INXNmhnSiB/3TUnHix2vVKjosjMTCk/CjfzXV2H41WPOLZ2/Pi3SxCicWIRj4kCcWhkEuIF2jGkg1+jmNiCl/zNMaBOAIP3QbDPtqOWbYlPd2YIzdj6WQ6R4",
+ "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAE="
+}
diff --git a/test/browser-bundle/webrtc-star-only.js b/test/browser-bundle/webrtc-star-only.js
new file mode 100644
index 0000000000..6fbe112f84
--- /dev/null
+++ b/test/browser-bundle/webrtc-star-only.js
@@ -0,0 +1,121 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const PeerInfo = require('peer-info')
+const PeerId = require('peer-id')
+const parallel = require('async/parallel')
+const pull = require('pull-stream')
+
+const Node = require('./browser-bundle')
+
+describe('libp2p-ipfs-browser (webrtc only)', () => {
+ let peer1
+ let peer2
+ let node1
+ let node2
+
+ it('create two peerInfo with webrtc-star addrs', (done) => {
+ parallel([
+ (cb) => PeerId.create({ bits: 1024 }, cb),
+ (cb) => PeerId.create({ bits: 1024 }, cb)
+ ], (err, ids) => {
+ expect(err).to.not.exist()
+
+ peer1 = new PeerInfo(ids[0])
+ const ma1 = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + ids[0].toB58String()
+ peer1.multiaddrs.add(ma1)
+
+ peer2 = new PeerInfo(ids[1])
+ const ma2 = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + ids[1].toB58String()
+ peer2.multiaddrs.add(ma2)
+
+ done()
+ })
+ })
+
+ it('create two libp2p nodes with those peers', (done) => {
+ node1 = new Node(peer1, null, { webRTCStar: true })
+ node2 = new Node(peer2, null, { webRTCStar: true })
+ done()
+ })
+
+ it('listen on the two libp2p nodes', (done) => {
+ parallel([
+ (cb) => node1.start(cb),
+ (cb) => node2.start(cb)
+ ], done)
+ })
+
+ it('handle a protocol on the first node', () => {
+ node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
+ })
+
+ it('dial from the second node to the first node', (done) => {
+ node1.dial(peer2, '/echo/1.0.0', (err, conn) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ const text = 'hello'
+ const peers1 = node1.peerBook.getAll()
+ expect(Object.keys(peers1)).to.have.length(1)
+
+ const peers2 = node2.peerBook.getAll()
+ expect(Object.keys(peers2)).to.have.length(1)
+
+ pull(
+ pull.values([Buffer(text)]),
+ conn,
+ pull.collect((err, data) => {
+ expect(err).to.not.exist()
+ expect(data[0].toString()).to.equal(text)
+ done()
+ })
+ )
+ }
+ })
+ })
+
+ it('node1 hangUp node2', (done) => {
+ node1.hangUp(peer2, (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ const peers = node1.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(node1.swarm.muxedConns)).to.have.length(0)
+ done()
+ }
+ })
+ })
+
+ it('create a third node and check that discovery works', (done) => {
+ let counter = 0
+
+ function check () {
+ if (++counter === 3) {
+ expect(Object.keys(node1.swarm.muxedConns).length).to.equal(1)
+ expect(Object.keys(node2.swarm.muxedConns).length).to.equal(1)
+ done()
+ }
+ }
+
+ PeerId.create((err, id3) => {
+ expect(err).to.not.exist()
+
+ const peer3 = new PeerInfo(id3)
+ const ma3 = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + id3.toB58String()
+ peer3.multiaddrs.add(ma3)
+
+ node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check))
+ node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check))
+
+ const node3 = new Node(peer3, null, { webRTCStar: true })
+ node3.start(check)
+ })
+ })
+})
diff --git a/test/browser-bundle/websockets-only.js b/test/browser-bundle/websockets-only.js
new file mode 100644
index 0000000000..34b7ea3aaa
--- /dev/null
+++ b/test/browser-bundle/websockets-only.js
@@ -0,0 +1,201 @@
+/* eslint max-nested-callbacks: ["error", 8] */
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const PeerInfo = require('peer-info')
+const PeerId = require('peer-id')
+const pull = require('pull-stream')
+const goodbye = require('pull-goodbye')
+const serializer = require('pull-serializer')
+const Buffer = require('safe-buffer').Buffer
+
+const Node = require('./browser-bundle')
+const rawPeer = require('./peer.json')
+
+describe('libp2p-ipfs-browser (websockets only)', () => {
+ let peerB
+ let nodeA
+
+ before((done) => {
+ const ma = '/ip4/127.0.0.1/tcp/9200/ws/ipfs/' + rawPeer.id
+
+ PeerId.createFromPrivKey(rawPeer.privKey, (err, id) => {
+ if (err) {
+ return done(err)
+ }
+
+ peerB = new PeerInfo(id)
+ peerB.multiaddrs.add(ma)
+ done()
+ })
+ })
+
+ after((done) => nodeA.stop(done))
+
+ it('create libp2pNode', (done) => {
+ PeerInfo.create((err, peerInfo) => {
+ expect(err).to.not.exist()
+ peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
+
+ nodeA = new Node(peerInfo)
+ done()
+ })
+ })
+
+ it('create libp2pNode with multiplex only', (done) => {
+ PeerInfo.create((err, peerInfo) => {
+ expect(err).to.not.exist()
+
+ const b = new Node(peerInfo, null, { muxer: ['multiplex'] })
+ expect(b.modules.connection.muxer).to.eql([require('libp2p-multiplex')])
+ done()
+ })
+ })
+
+ it('start libp2pNode', (done) => {
+ nodeA.start(done)
+ })
+
+ // General connectivity tests
+
+ it('libp2p.dial using Multiaddr nodeA to nodeB', (done) => {
+ nodeA.dial(peerB.multiaddrs.toArray()[0], (err) => {
+ expect(err).to.not.exist()
+
+ setTimeout(check, 500) // Some time for Identify to finish
+
+ function check () {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ done()
+ }
+ })
+ })
+
+ it('libp2p.dial using Multiaddr on Protocol nodeA to nodeB', (done) => {
+ nodeA.dial(peerB.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => {
+ expect(err).to.not.exist()
+
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ pull(
+ pull.values([Buffer.from('hey')]),
+ conn,
+ pull.collect((err, data) => {
+ expect(err).to.not.exist()
+ expect(data).to.eql([Buffer.from('hey')])
+ done()
+ })
+ )
+ })
+ })
+
+ it('libp2p.hangUp using Multiaddr nodeA to nodeB', (done) => {
+ nodeA.hangUp(peerB.multiaddrs.toArray()[0], (err) => {
+ expect(err).to.not.exist()
+
+ setTimeout(check, 500)
+
+ function check () {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
+ done()
+ }
+ })
+ })
+
+ it('libp2p.dial using PeerInfo nodeA to nodeB', (done) => {
+ nodeA.dial(peerB, (err) => {
+ expect(err).to.not.exist()
+
+ setTimeout(check, 500) // Some time for Identify to finish
+
+ function check () {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ done()
+ }
+ })
+ })
+
+ it('libp2p.dial using PeerInfo on Protocol nodeA to nodeB', (done) => {
+ nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => {
+ expect(err).to.not.exist()
+ const peers = nodeA.peerBook.getAll()
+ expect(err).to.not.exist()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ pull(
+ pull.values([Buffer.from('hey')]),
+ conn,
+ pull.collect((err, data) => {
+ expect(err).to.not.exist()
+ expect(data).to.eql([Buffer.from('hey')])
+ done()
+ })
+ )
+ })
+ })
+
+ it('libp2p.hangUp using PeerInfo nodeA to nodeB', (done) => {
+ nodeA.hangUp(peerB, (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ const peers = nodeA.peerBook.getAll()
+ expect(err).to.not.exist()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
+ done()
+ }
+ })
+ })
+
+ describe('stress', () => {
+ it('one big write', (done) => {
+ nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => {
+ expect(err).to.not.exist()
+ const rawMessage = Buffer.alloc(100000)
+ rawMessage.fill('a')
+
+ const s = serializer(goodbye({
+ source: pull.values([rawMessage]),
+ sink: pull.collect((err, results) => {
+ expect(err).to.not.exist()
+ expect(results).to.have.length(1)
+ expect(Buffer.from(results[0])).to.have.length(rawMessage.length)
+ done()
+ })
+ }))
+ pull(s, conn, s)
+ })
+ })
+
+ it('many writes', (done) => {
+ nodeA.dial(peerB, '/echo/1.0.0', (err, conn) => {
+ expect(err).to.not.exist()
+
+ const s = serializer(goodbye({
+ source: pull(
+ pull.infinite(),
+ pull.take(1000),
+ pull.map((val) => Buffer.from(val.toString()))
+ ),
+ sink: pull.collect((err, result) => {
+ expect(err).to.not.exist()
+ expect(result).to.have.length(1000)
+ done()
+ })
+ }))
+
+ pull(s, conn, s)
+ })
+ })
+ })
+})
diff --git a/test/browser.js b/test/browser.js
new file mode 100644
index 0000000000..6bbaf816a9
--- /dev/null
+++ b/test/browser.js
@@ -0,0 +1,7 @@
+'use strict'
+
+const w = require('webrtcsupport')
+
+require('./base')
+require('./browser-bundle/websockets-only')
+if (w.support) { require('./browser-bundle/webrtc-star-only') }
diff --git a/test/node.js b/test/node.js
new file mode 100644
index 0000000000..f66c017b50
--- /dev/null
+++ b/test/node.js
@@ -0,0 +1,10 @@
+'use strict'
+
+require('./base')
+require('./nodejs-bundle/tcp')
+require('./nodejs-bundle/tcp+websockets')
+require('./nodejs-bundle/tcp+websockets+webrtc-star')
+require('./nodejs-bundle/stream-muxing')
+require('./nodejs-bundle/discovery')
+require('./nodejs-bundle/peer-routing')
+require('./nodejs-bundle/content-routing')
diff --git a/test/nodejs-bundle/content-routing.js b/test/nodejs-bundle/content-routing.js
new file mode 100644
index 0000000000..6a46a2aaa1
--- /dev/null
+++ b/test/nodejs-bundle/content-routing.js
@@ -0,0 +1,90 @@
+/* eslint-env mocha */
+/* eslint max-nested-callbacks: ["error", 8] */
+
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const parallel = require('async/parallel')
+const utils = require('./utils')
+const createNode = utils.createNode
+const _times = require('lodash.times')
+const CID = require('cids')
+
+describe('.contentRouting', () => {
+ let nodeA
+ let nodeB
+ let nodeC
+ let nodeD
+ let nodeE
+
+ before((done) => {
+ const tasks = _times(5, () => (cb) => {
+ createNode('/ip4/0.0.0.0/tcp/0', {
+ mdns: false,
+ dht: true
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ node.start((err) => cb(err, node))
+ })
+ })
+
+ parallel(tasks, (err, nodes) => {
+ expect(err).to.not.exist()
+ nodeA = nodes[0]
+ nodeB = nodes[1]
+ nodeC = nodes[2]
+ nodeD = nodes[3]
+ nodeE = nodes[4]
+
+ parallel([
+ (cb) => nodeA.dial(nodeB.peerInfo, cb),
+ (cb) => nodeB.dial(nodeC.peerInfo, cb),
+ (cb) => nodeC.dial(nodeD.peerInfo, cb),
+ (cb) => nodeD.dial(nodeE.peerInfo, cb),
+ (cb) => nodeE.dial(nodeA.peerInfo, cb)
+ ], done)
+ })
+ })
+
+ after((done) => {
+ parallel([
+ (cb) => nodeA.stop(cb),
+ (cb) => nodeB.stop(cb),
+ (cb) => nodeC.stop(cb),
+ (cb) => nodeD.stop(cb),
+ (cb) => nodeE.stop(cb)
+ ], done)
+ })
+
+ describe('le ring', () => {
+ const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
+
+ it('let kbucket get filled', (done) => {
+ setTimeout(() => done(), 250)
+ })
+
+ it('nodeA.contentRouting.provide', (done) => {
+ nodeA.contentRouting.provide(cid, done)
+ })
+
+ it('nodeE.contentRouting.findProviders for existing record', (done) => {
+ nodeE.contentRouting.findProviders(cid, 5000, (err, providers) => {
+ expect(err).to.not.exist()
+ expect(providers).to.have.length.above(0)
+ done()
+ })
+ })
+
+ it('nodeC.contentRouting.findProviders for non existing record (timeout)', (done) => {
+ const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSnnnn')
+
+ nodeE.contentRouting.findProviders(cid, 5000, (err, providers) => {
+ expect(err).to.not.exist()
+ expect(providers).to.have.length(0)
+ done()
+ })
+ })
+ })
+})
diff --git a/test/nodejs-bundle/discovery.js b/test/nodejs-bundle/discovery.js
new file mode 100644
index 0000000000..4cdab4560c
--- /dev/null
+++ b/test/nodejs-bundle/discovery.js
@@ -0,0 +1,99 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const signalling = require('libp2p-webrtc-star/src/sig-server')
+const parallel = require('async/parallel')
+const utils = require('./utils')
+const createNode = utils.createNode
+const echo = utils.echo
+
+describe('discovery', () => {
+ let nodeA
+ let nodeB
+ let port = 24642
+ let ss
+
+ function setup (options) {
+ before((done) => {
+ port++
+ parallel([
+ (cb) => {
+ signalling.start({ port: port }, (err, server) => {
+ expect(err).to.not.exist()
+ ss = server
+ cb()
+ })
+ },
+ (cb) => createNode([
+ '/ip4/0.0.0.0/tcp/0',
+ `/libp2p-webrtc-star/ip4/127.0.0.1/tcp/${port}/ws`
+ ], options, (err, node) => {
+ expect(err).to.not.exist()
+ nodeA = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode([
+ '/ip4/0.0.0.0/tcp/0',
+ `/libp2p-webrtc-star/ip4/127.0.0.1/tcp/${port}/ws`
+ ], options, (err, node) => {
+ expect(err).to.not.exist()
+ nodeB = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ ], done)
+ })
+
+ after((done) => {
+ parallel([
+ (cb) => nodeA.stop(cb),
+ (cb) => nodeB.stop(cb),
+ (cb) => ss.stop(done)
+ ], done)
+ })
+ }
+
+ describe('MulticastDNS', () => {
+ setup({ mdns: true })
+
+ it('find a peer', (done) => {
+ nodeA.once('peer:discovery', (peerInfo) => {
+ expect(nodeB.peerInfo.id.toB58String())
+ .to.eql(peerInfo.id.toB58String())
+ done()
+ })
+ })
+ })
+
+ // TODO needs a delay (this test is already long)
+ describe.skip('WebRTCStar', () => {
+ setup({ webRTCStar: true })
+
+ it('find a peer', (done) => {
+ nodeA.once('peer:discovery', (peerInfo) => {
+ expect(nodeB.peerInfo.id.toB58String())
+ .to.eql(peerInfo.id.toB58String())
+ done()
+ })
+ })
+ })
+
+ describe('MulticastDNS + WebRTCStar', () => {
+ setup({
+ webRTCStar: true,
+ mdns: true
+ })
+
+ it('find a peer', (done) => {
+ nodeA.once('peer:discovery', (peerInfo) => {
+ expect(nodeB.peerInfo.id.toB58String())
+ .to.eql(peerInfo.id.toB58String())
+ done()
+ })
+ })
+ })
+})
diff --git a/test/nodejs-bundle/nodejs-bundle.js b/test/nodejs-bundle/nodejs-bundle.js
new file mode 100644
index 0000000000..5d67075210
--- /dev/null
+++ b/test/nodejs-bundle/nodejs-bundle.js
@@ -0,0 +1,80 @@
+'use strict'
+
+const TCP = require('libp2p-tcp')
+const MulticastDNS = require('libp2p-mdns')
+const WS = require('libp2p-websockets')
+const Railing = require('libp2p-railing')
+const spdy = require('libp2p-spdy')
+const KadDHT = require('libp2p-kad-dht')
+const multiplex = require('libp2p-multiplex')
+const secio = require('libp2p-secio')
+const libp2p = require('../..')
+
+function mapMuxers (list) {
+ return list.map((pref) => {
+ if (typeof pref !== 'string') {
+ return pref
+ }
+ switch (pref.trim().toLowerCase()) {
+ case 'spdy': return spdy
+ case 'multiplex': return multiplex
+ default:
+ throw new Error(pref + ' muxer not available')
+ }
+ })
+}
+
+function getMuxers (muxers) {
+ const muxerPrefs = process.env.LIBP2P_MUXER
+ if (muxerPrefs && !muxers) {
+ return mapMuxers(muxerPrefs.split(','))
+ } else if (muxers) {
+ return mapMuxers(muxers)
+ } else {
+ return [multiplex, spdy]
+ }
+}
+
+class Node extends libp2p {
+ constructor (peerInfo, peerBook, options) {
+ options = options || {}
+
+ const modules = {
+ transport: [
+ new TCP(),
+ new WS()
+ ],
+ connection: {
+ muxer: getMuxers(options.muxer),
+ crypto: [ secio ]
+ },
+ discovery: []
+ }
+
+ if (options.dht) {
+ modules.DHT = KadDHT
+ }
+
+ if (options.mdns) {
+ const mdns = new MulticastDNS(peerInfo, 'ipfs.local')
+ modules.discovery.push(mdns)
+ }
+
+ if (options.bootstrap) {
+ const r = new Railing(options.bootstrap)
+ modules.discovery.push(r)
+ }
+
+ if (options.modules && options.modules.transport) {
+ options.modules.transport.forEach((t) => modules.transport.push(t))
+ }
+
+ if (options.modules && options.modules.discovery) {
+ options.modules.discovery.forEach((d) => modules.discovery.push(d))
+ }
+
+ super(modules, peerInfo, peerBook, options)
+ }
+}
+
+module.exports = Node
diff --git a/test/nodejs-bundle/peer-routing.js b/test/nodejs-bundle/peer-routing.js
new file mode 100644
index 0000000000..03243102e8
--- /dev/null
+++ b/test/nodejs-bundle/peer-routing.js
@@ -0,0 +1,94 @@
+/* eslint-env mocha */
+/* eslint max-nested-callbacks: ["error", 8] */
+
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const parallel = require('async/parallel')
+const utils = require('./utils')
+const createNode = utils.createNode
+const _times = require('lodash.times')
+
+describe('.peerRouting', () => {
+ let nodeA
+ let nodeB
+ let nodeC
+ let nodeD
+ let nodeE
+
+ before((done) => {
+ const tasks = _times(5, () => (cb) => {
+ createNode('/ip4/0.0.0.0/tcp/0', {
+ mdns: false,
+ dht: true
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ node.start((err) => cb(err, node))
+ })
+ })
+
+ parallel(tasks, (err, nodes) => {
+ expect(err).to.not.exist()
+ nodeA = nodes[0]
+ nodeB = nodes[1]
+ nodeC = nodes[2]
+ nodeD = nodes[3]
+ nodeE = nodes[4]
+
+ parallel([
+ (cb) => nodeA.dial(nodeB.peerInfo, cb),
+ (cb) => nodeB.dial(nodeC.peerInfo, cb),
+ (cb) => nodeC.dial(nodeD.peerInfo, cb),
+ (cb) => nodeD.dial(nodeE.peerInfo, cb),
+ (cb) => nodeE.dial(nodeA.peerInfo, cb)
+ ], done)
+ })
+ })
+
+ after((done) => {
+ parallel([
+ (cb) => nodeA.stop(cb),
+ (cb) => nodeB.stop(cb),
+ (cb) => nodeC.stop(cb),
+ (cb) => nodeD.stop(cb),
+ (cb) => nodeE.stop(cb)
+ ], done)
+ })
+
+ describe('el ring', () => {
+ it('let kbucket get filled', (done) => {
+ setTimeout(() => done(), 250)
+ })
+
+ it('nodeA.dial by Id to node C', (done) => {
+ nodeA.dial(nodeC.peerInfo.id, (err) => {
+ expect(err).to.not.exist()
+ done()
+ })
+ })
+
+ it('nodeB.dial by Id to node D', (done) => {
+ nodeB.dial(nodeD.peerInfo.id, (err) => {
+ expect(err).to.not.exist()
+ done()
+ })
+ })
+
+ it('nodeC.dial by Id to node E', (done) => {
+ nodeC.dial(nodeE.peerInfo.id, (err) => {
+ expect(err).to.not.exist()
+ done()
+ })
+ })
+
+ it('nodeB.peerRouting.findPeer(nodeE.peerInfo.id)', (done) => {
+ nodeB.peerRouting.findPeer(nodeE.peerInfo.id, (err, peerInfo) => {
+ expect(err).to.not.exist()
+ expect(nodeE.peerInfo.id.toB58String()).to.equal(peerInfo.id.toB58String())
+ done()
+ })
+ })
+ })
+})
diff --git a/test/nodejs-bundle/spawn-libp2p-node.js b/test/nodejs-bundle/spawn-libp2p-node.js
new file mode 100755
index 0000000000..cd412074cb
--- /dev/null
+++ b/test/nodejs-bundle/spawn-libp2p-node.js
@@ -0,0 +1,30 @@
+#! /usr/bin/env node
+
+'use strict'
+
+const Node = require('./nodejs-bundle')
+const PeerInfo = require('peer-info')
+const PeerId = require('peer-id')
+const pull = require('pull-stream')
+
+const idBak = require('./test-data/test-id.json')
+
+PeerId.createFromJSON(idBak, (err, peerId) => {
+ if (err) {
+ throw err
+ }
+
+ const peerInfo = new PeerInfo(peerId)
+
+ peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/12345')
+
+ const node = new Node(peerInfo)
+
+ node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn))
+
+ node.start((err) => {
+ if (err) { throw err }
+
+ console.log('Spawned node started, env:', process.env.LIBP2P_MUXER)
+ })
+})
diff --git a/test/nodejs-bundle/stream-muxing.js b/test/nodejs-bundle/stream-muxing.js
new file mode 100644
index 0000000000..72304245ed
--- /dev/null
+++ b/test/nodejs-bundle/stream-muxing.js
@@ -0,0 +1,207 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const parallel = require('async/parallel')
+const series = require('async/series')
+const pull = require('pull-stream')
+const utils = require('./utils')
+const createNode = utils.createNode
+const echo = utils.echo
+
+function test (nodeA, nodeB, callback) {
+ nodeA.dial(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => {
+ expect(err).to.not.exist()
+
+ pull(
+ pull.values([new Buffer('hey')]),
+ conn,
+ pull.collect((err, data) => {
+ expect(err).to.not.exist()
+ expect(data).to.be.eql([new Buffer('hey')])
+ callback()
+ })
+ )
+ })
+}
+
+function teardown (nodeA, nodeB, callback) {
+ parallel([
+ (cb) => nodeA.stop(cb),
+ (cb) => nodeB.stop(cb)
+ ], callback)
+}
+
+describe('stream muxing', (done) => {
+ it('spdy only', (done) => {
+ let nodeA
+ let nodeB
+
+ function setup (callback) {
+ parallel([
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['spdy']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeA = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['spdy']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeB = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ ], callback)
+ }
+
+ series([
+ (cb) => setup(cb),
+ (cb) => test(nodeA, nodeB, cb),
+ (cb) => teardown(nodeA, nodeB, cb)
+ ], done)
+ })
+
+ it('multiplex only', (done) => {
+ let nodeA
+ let nodeB
+
+ function setup (callback) {
+ parallel([
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['multiplex']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeA = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['multiplex']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeB = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ ], callback)
+ }
+
+ series([
+ (cb) => setup(cb),
+ (cb) => test(nodeA, nodeB, cb),
+ (cb) => teardown(nodeA, nodeB, cb)
+ ], done)
+ })
+
+ it('spdy + multiplex', (done) => {
+ let nodeA
+ let nodeB
+
+ function setup (callback) {
+ parallel([
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['spdy', 'multiplex']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeA = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['spdy', 'multiplex']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeB = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ ], callback)
+ }
+
+ series([
+ (cb) => setup(cb),
+ (cb) => test(nodeA, nodeB, cb),
+ (cb) => teardown(nodeA, nodeB, cb)
+ ], done)
+ })
+
+ it('spdy + multiplex switched order', (done) => {
+ let nodeA
+ let nodeB
+
+ function setup (callback) {
+ parallel([
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['spdy', 'multiplex']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeA = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['multiplex', 'spdy']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeB = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ ], callback)
+ }
+
+ series([
+ (cb) => setup(cb),
+ (cb) => test(nodeA, nodeB, cb),
+ (cb) => teardown(nodeA, nodeB, cb)
+ ], done)
+ })
+
+ it('one without the other fails to establish a muxedConn', (done) => {
+ let nodeA
+ let nodeB
+
+ function setup (callback) {
+ parallel([
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['spdy']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeA = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', {
+ muxer: ['multiplex']
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeB = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ ], callback)
+ }
+
+ series([
+ (cb) => setup(cb),
+ (cb) => {
+ // it will just 'warm up a conn'
+ expect(Object.keys(nodeA.swarm.muxers)).to.have.length(1)
+ expect(Object.keys(nodeB.swarm.muxers)).to.have.length(1)
+
+ nodeA.dial(nodeB.peerInfo, (err) => {
+ expect(err).to.not.exist()
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
+ cb()
+ })
+ },
+ (cb) => teardown(nodeA, nodeB, cb)
+ ], done)
+ })
+})
diff --git a/test/nodejs-bundle/tcp+websockets+webrtc-star.js b/test/nodejs-bundle/tcp+websockets+webrtc-star.js
new file mode 100644
index 0000000000..a7992f3861
--- /dev/null
+++ b/test/nodejs-bundle/tcp+websockets+webrtc-star.js
@@ -0,0 +1,246 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const parallel = require('async/parallel')
+const signalling = require('libp2p-webrtc-star/src/sig-server')
+const WStar = require('libp2p-webrtc-star')
+const wrtc = require('wrtc')
+const utils = require('./utils')
+const createNode = utils.createNode
+const echo = utils.echo
+
+describe('TCP + WebSockets + WebRTCStar', () => {
+ let nodeAll
+ let nodeTCP
+ let nodeWS
+ let nodeWStar
+
+ let ss
+
+ before((done) => {
+ parallel([
+ (cb) => {
+ signalling.start({ port: 24642 }, (err, server) => {
+ expect(err).to.not.exist()
+ ss = server
+ cb()
+ })
+ },
+ (cb) => {
+ const wstar = new WStar({wrtc: wrtc})
+ createNode([
+ '/ip4/0.0.0.0/tcp/0',
+ '/ip4/127.0.0.1/tcp/25011/ws',
+ '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/24642/ws'
+ ], {
+ modules: {
+ transport: [wstar],
+ discovery: [wstar.discovery]
+ }
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeAll = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ },
+ (cb) => createNode([
+ '/ip4/0.0.0.0/tcp/0'
+ ], (err, node) => {
+ expect(err).to.not.exist()
+ nodeTCP = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode([
+ '/ip4/127.0.0.1/tcp/25022/ws'
+ ], (err, node) => {
+ expect(err).to.not.exist()
+ nodeWS = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+
+ (cb) => {
+ const wstar = new WStar({wrtc: wrtc})
+
+ createNode([
+ '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/24642/ws'
+ ], {
+ modules: {
+ transport: [wstar],
+ discovery: [wstar.discovery]
+ }
+ }, (err, node) => {
+ expect(err).to.not.exist()
+ nodeWStar = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ }
+ ], done)
+ })
+
+ after((done) => {
+ parallel([
+ (cb) => nodeAll.stop(cb),
+ (cb) => nodeTCP.stop(cb),
+ (cb) => nodeWS.stop(cb),
+ (cb) => nodeWStar.stop(cb),
+ (cb) => ss.stop(done)
+ ], done)
+ })
+
+ it('nodeAll.dial nodeTCP using PeerInfo', (done) => {
+ nodeAll.dial(nodeTCP.peerInfo, (err) => {
+ expect(err).to.not.exist()
+
+ // Some time for Identify to finish
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeAll.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(1)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeTCP.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(1)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeAll.hangUp nodeTCP using PeerInfo', (done) => {
+ nodeAll.hangUp(nodeTCP.peerInfo, (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeAll.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(0)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeTCP.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(0)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeAll.dial nodeWS using PeerInfo', (done) => {
+ nodeAll.dial(nodeWS.peerInfo, (err) => {
+ expect(err).to.not.exist()
+
+ // Some time for Identify to finish
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeAll.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(2)
+ expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(1)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeWS.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(1)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeAll.hangUp nodeWS using PeerInfo', (done) => {
+ nodeAll.hangUp(nodeWS.peerInfo, (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeAll.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(2)
+ expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(0)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeWS.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(0)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeAll.dial nodeWStar using PeerInfo', (done) => {
+ nodeAll.dial(nodeWStar.peerInfo, (err) => {
+ expect(err).to.not.exist()
+
+ // Some time for Identify to finish
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeAll.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(3)
+ expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(1)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeWStar.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(1)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeAll.hangUp nodeWStar using PeerInfo', (done) => {
+ nodeAll.hangUp(nodeWStar.peerInfo, (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeAll.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(3)
+ expect(Object.keys(nodeAll.swarm.muxedConns)).to.have.length(0)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeWStar.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeWStar.swarm.muxedConns)).to.have.length(0)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+})
diff --git a/test/nodejs-bundle/tcp+websockets.js b/test/nodejs-bundle/tcp+websockets.js
new file mode 100644
index 0000000000..609eedea27
--- /dev/null
+++ b/test/nodejs-bundle/tcp+websockets.js
@@ -0,0 +1,167 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const parallel = require('async/parallel')
+// const multiaddr = require('multiaddr')
+// const pull = require('pull-stream')
+const utils = require('./utils')
+const createNode = utils.createNode
+const echo = utils.echo
+
+describe('TCP + WebSockets', () => {
+ let nodeTCP
+ let nodeTCPnWS
+ let nodeWS
+
+ before((done) => {
+ parallel([
+ (cb) => createNode([
+ '/ip4/0.0.0.0/tcp/0'
+ ], (err, node) => {
+ expect(err).to.not.exist()
+ nodeTCP = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode([
+ '/ip4/0.0.0.0/tcp/0',
+ '/ip4/127.0.0.1/tcp/25011/ws'
+ ], (err, node) => {
+ expect(err).to.not.exist()
+ nodeTCPnWS = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode([
+ '/ip4/127.0.0.1/tcp/25022/ws'
+ ], (err, node) => {
+ expect(err).to.not.exist()
+ nodeWS = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ ], done)
+ })
+
+ after((done) => {
+ parallel([
+ (cb) => nodeTCP.stop(cb),
+ (cb) => nodeTCPnWS.stop(cb),
+ (cb) => nodeWS.stop(cb)
+ ], done)
+ })
+
+ it('nodeTCP.dial nodeTCPnWS using PeerInfo', (done) => {
+ nodeTCP.dial(nodeTCPnWS.peerInfo, (err) => {
+ expect(err).to.not.exist()
+
+ // Some time for Identify to finish
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeTCP.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(1)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeTCPnWS.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(1)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeTCP.hangUp nodeTCPnWS using PeerInfo', (done) => {
+ nodeTCP.hangUp(nodeTCPnWS.peerInfo, (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeTCP.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeTCP.swarm.muxedConns)).to.have.length(0)
+
+ cb()
+ },
+ (cb) => {
+ const peers = nodeTCPnWS.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(0)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeTCPnWS.dial nodeWS using PeerInfo', (done) => {
+ nodeTCPnWS.dial(nodeWS.peerInfo, (err) => {
+ expect(err).to.not.exist()
+
+ // Some time for Identify to finish
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeTCPnWS.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(2)
+ expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(1)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeWS.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(1)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeTCPnWS.hangUp nodeWS using PeerInfo', (done) => {
+ nodeTCPnWS.hangUp(nodeWS.peerInfo, (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeTCPnWS.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(2)
+ expect(Object.keys(nodeTCPnWS.swarm.muxedConns)).to.have.length(0)
+
+ cb()
+ },
+ (cb) => {
+ const peers = nodeWS.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeWS.swarm.muxedConns)).to.have.length(0)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ // Until https://github.com/libp2p/js-libp2p/issues/46 is resolved
+ // Everynode will be able to dial in WebSockets
+ it.skip('nodeTCP.dial nodeWS using PeerInfo is unsuccesful', (done) => {
+ nodeTCP.dial(nodeWS.peerInfo, (err) => {
+ expect(err).to.exist()
+ done()
+ })
+ })
+})
diff --git a/test/nodejs-bundle/tcp.js b/test/nodejs-bundle/tcp.js
new file mode 100644
index 0000000000..36fe530a39
--- /dev/null
+++ b/test/nodejs-bundle/tcp.js
@@ -0,0 +1,234 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const parallel = require('async/parallel')
+const series = require('async/series')
+const pull = require('pull-stream')
+const utils = require('./utils')
+const createNode = utils.createNode
+const echo = utils.echo
+
+describe('TCP only', () => {
+ let nodeA
+ let nodeB
+
+ before((done) => {
+ parallel([
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => {
+ expect(err).to.not.exist()
+ nodeA = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ }),
+ (cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => {
+ expect(err).to.not.exist()
+ nodeB = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(cb)
+ })
+ ], done)
+ })
+
+ after((done) => {
+ parallel([
+ (cb) => nodeA.stop(cb),
+ (cb) => nodeB.stop(cb)
+ ], done)
+ })
+
+ it('nodeA.dial nodeB using PeerInfo without proto (warmup)', (done) => {
+ nodeA.dial(nodeB.peerInfo, (err) => {
+ expect(err).to.not.exist()
+
+ // Some time for Identify to finish
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeA.peerBook.getAll()
+ expect(err).to.not.exist()
+ expect(Object.keys(peers)).to.have.length(1)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeB.peerBook.getAll()
+ expect(err).to.not.exist()
+ expect(Object.keys(peers)).to.have.length(1)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeA.dial nodeB using PeerInfo', (done) => {
+ nodeA.dial(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => {
+ expect(err).to.not.exist()
+
+ pull(
+ pull.values([new Buffer('hey')]),
+ conn,
+ pull.collect((err, data) => {
+ expect(err).to.not.exist()
+ expect(data).to.be.eql([new Buffer('hey')])
+ done()
+ })
+ )
+ })
+ })
+
+ it('nodeA.hangUp nodeB using PeerInfo (first)', (done) => {
+ nodeA.hangUp(nodeB.peerInfo, (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeB.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeA.dial nodeB using multiaddr', (done) => {
+ nodeA.dial(nodeB.peerInfo.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => {
+ // Some time for Identify to finish
+ setTimeout(check, 500)
+
+ function check () {
+ expect(err).to.not.exist()
+ series([
+ (cb) => {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeB.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1)
+ cb()
+ }
+ ], () => {
+ pull(
+ pull.values([new Buffer('hey')]),
+ conn,
+ pull.collect((err, data) => {
+ expect(err).to.not.exist()
+ expect(data).to.be.eql([new Buffer('hey')])
+ done()
+ })
+ )
+ })
+ }
+ })
+ })
+
+ it('nodeA.hangUp nodeB using multiaddr (second)', (done) => {
+ nodeA.hangUp(nodeB.peerInfo.multiaddrs.toArray()[0], (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeB.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+
+ it('nodeA.dial nodeB using PeerId', (done) => {
+ nodeA.dial(nodeB.peerInfo.id, '/echo/1.0.0', (err, conn) => {
+ // Some time for Identify to finish
+ setTimeout(check, 500)
+
+ function check () {
+ expect(err).to.not.exist()
+ series([
+ (cb) => {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeB.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(1)
+ cb()
+ }
+ ], () => {
+ pull(
+ pull.values([new Buffer('hey')]),
+ conn,
+ pull.collect((err, data) => {
+ expect(err).to.not.exist()
+ expect(data).to.be.eql([new Buffer('hey')])
+ done()
+ })
+ )
+ })
+ }
+ })
+ })
+
+ it('nodeA.hangUp nodeB using PeerId (third)', (done) => {
+ nodeA.hangUp(nodeB.peerInfo.multiaddrs.toArray()[0], (err) => {
+ expect(err).to.not.exist()
+ setTimeout(check, 500)
+
+ function check () {
+ parallel([
+ (cb) => {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
+ cb()
+ },
+ (cb) => {
+ const peers = nodeB.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+
+ expect(Object.keys(nodeB.swarm.muxedConns)).to.have.length(0)
+ cb()
+ }
+ ], done)
+ }
+ })
+ })
+})
diff --git a/test/nodejs-bundle/test-data/test-id.json b/test/nodejs-bundle/test-data/test-id.json
new file mode 100644
index 0000000000..0b37a6cc9c
--- /dev/null
+++ b/test/nodejs-bundle/test-data/test-id.json
@@ -0,0 +1,5 @@
+{
+ "id": "QmaG17D4kfTB2RNUCr16bSfVvUVt2Xn3rPYeqQDvnVcXFr",
+ "privKey": "CAASqAkwggSkAgEAAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAECggEAVE1mgGo58LJknml0WNn8tS5rfEiF5AhhPyOwvBTy04nDYFgZEykxgjTkrSbqgzfmYmOjSDICJUyNXGHISYqDz4CXOyBY9U0RuWeWp58BjVan75N4bRB+VNbHk9HbDkYEQlSoCW9ze0aRfvVa4v5QdRLSDMhwN+stokrsYcX/WIWYTM2e2jW+qQOzS8SJl7wYsgtd3WikrxwXkRL3sCMHEcgcPhoKacoD5Yr9cB0IC5vzhu4t/WMa+N2UEndcKGAbXsh8kA7BPFM6lqnEpOHpWEVEAYasAwFGUvUN9GwhtqpaNNS2sG6Nrz95cC99Nqx58uIXcTAJm3Fh/WfKJ6I1xQKBgQD+g7A5OSWw+i/zhTKVPJg93/eohViL0dGZT9Tf0/VslsFl00FwnZmBKA6BJ6ZL3hD00OcqIL3m6EzZ4q38U97XZxf2OUsPPJtl+Avqtlk16AHRHB9I17LGXJ30xZRkxL665oLms0D2T4NIZZX/uVMoS18lRvCZj1aEYQFCrZYgowKBgQDCxtA695S0vl6E3Q4G6MrDZK+2JqjaGL0XTnpHWiAjnk2lnV2CCZnWpEHT+ebF2fWx5nYQo5sugc6bS+4k9jRNUgxf2sQieZYCBjbnjCEVrPTm/dPTkaw1CQ/ox5/R1/Elbw8vteF9uUAvR0FL8Ss1Dqw6B2SxdTowxMy6qQ7sNwKBgG2N3eMj2DeP2egm45kdliK8L2yYyX6V+HTXyjf2kuQFGIZuIvMIw7S2u1eY65ooon/fFEIsCdJFGB+J1X6R05BAzi2sh8StP+7qkKadi1UK4w1R352JS2jbIRrlmXSuw7LL2njXnBTqMQaOw7xp14O2vePb32EaNBGTd+ltsvulAoGBALGIc4370oA4MIDb2Ag2MXKNmJbnf+piuB/BOTVGEZtFlDKLUArR43W/+/xRgKX/97FyhVS/OxfV21Kzj9oCy0NasMrB5RojRraLoYnFsPZH0mWlIGlsEtG4c9bR9XtYX4WmR+pN1r04mCc/xGWK6b4PpK2zxXT2i9ad2pmctGxbAoGBAIcp0UML5QCqvLmcob2/6PCRaYAxJBb9lDqOHredMgQih2hGnHFCyKk9eBAbFf/KN0guJTBDaAJRclcxsLLn7rV6grMNt+0EUepm7tWT0z5j8gNGbGGhuGDdqcmfJTc2EMdQrfhzYDN3rL1v3l+Ujwla2khL2ozE7SQ/KVeA1saY",
+ "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAE="
+}
diff --git a/test/nodejs-bundle/turbolence.js b/test/nodejs-bundle/turbolence.js
new file mode 100644
index 0000000000..4455322697
--- /dev/null
+++ b/test/nodejs-bundle/turbolence.js
@@ -0,0 +1,84 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const expect = chai.expect
+const multiaddr = require('multiaddr')
+const spawn = require('child_process').spawn
+const path = require('path')
+// const map = require('async/map')
+const pull = require('pull-stream')
+const utils = require('./utils')
+const createNode = utils.createNode
+const echo = utils.echo
+
+describe('Turbolence tests', () => {
+ let nodeA
+ let nodeSpawn
+
+ before((done) => {
+ createNode('/ip4/0.0.0.0/tcp/0', (err, node) => {
+ expect(err).to.not.exist()
+ nodeA = node
+ node.handle('/echo/1.0.0', echo)
+ node.start(done)
+ })
+ })
+
+ after((done) => nodeA.stop(done))
+
+ it('spawn a node in a different process', (done) => {
+ const filePath = path.join(__dirname, './spawn-libp2p-node.js')
+
+ nodeSpawn = spawn(filePath, { env: process.env })
+
+ let spawned = false
+
+ nodeSpawn.stdout.on('data', (data) => {
+ // console.log(data.toString())
+ if (!spawned) {
+ spawned = true
+ done()
+ }
+ })
+
+ nodeSpawn.stderr.on('data', (data) => console.log(data.toString()))
+ })
+
+ it('connect nodeA to that node', (done) => {
+ const spawnedId = require('./test-data/test-id.json')
+ const maddr = multiaddr('/ip4/127.0.0.1/tcp/12345/ipfs/' + spawnedId.id)
+
+ nodeA.dial(maddr, '/echo/1.0.0', (err, conn) => {
+ expect(err).to.not.exist()
+ const peers = nodeA.peerBook.getAll()
+
+ expect(Object.keys(peers)).to.have.length(1)
+
+ pull(
+ pull.values([new Buffer('hey')]),
+ conn,
+ pull.collect((err, data) => {
+ expect(err).to.not.exist()
+ expect(data).to.eql([new Buffer('hey')])
+ done()
+ })
+ )
+ })
+ })
+
+ it('crash that node, ensure nodeA continues going steady', (done) => {
+ // TODO investigate why CI crashes
+ setTimeout(() => nodeSpawn.kill('SIGKILL'), 1000)
+ // nodeSpawn.kill('SIGKILL')
+ setTimeout(check, 5000)
+
+ function check () {
+ const peers = nodeA.peerBook.getAll()
+ expect(Object.keys(peers)).to.have.length(1)
+ expect(Object.keys(nodeA.swarm.muxedConns)).to.have.length(0)
+ done()
+ }
+ })
+})
diff --git a/test/nodejs-bundle/utils.js b/test/nodejs-bundle/utils.js
new file mode 100644
index 0000000000..4c26b66f9f
--- /dev/null
+++ b/test/nodejs-bundle/utils.js
@@ -0,0 +1,43 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+const Node = require('./nodejs-bundle')
+const PeerInfo = require('peer-info')
+const PeerId = require('peer-id')
+const waterfall = require('async/waterfall')
+const pull = require('pull-stream')
+
+function createNode (multiaddrs, options, callback) {
+ if (typeof options === 'function') {
+ callback = options
+ options = {}
+ }
+
+ if (!Array.isArray(multiaddrs)) {
+ multiaddrs = [multiaddrs]
+ }
+
+ waterfall([
+ (cb) => PeerId.create({ bits: 1024 }, cb),
+ (peerId, cb) => PeerInfo.create(peerId, cb),
+ (peerInfo, cb) => {
+ multiaddrs.map((ma) => peerInfo.multiaddrs.add(ma))
+ cb(null, peerInfo)
+ },
+ (peerInfo, cb) => {
+ const node = new Node(peerInfo, undefined, options)
+ cb(null, node)
+ }
+ ], callback)
+}
+
+function echo (protocol, conn) {
+ pull(conn, conn)
+}
+
+module.exports = {
+ createNode: createNode,
+ echo: echo
+}