mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-07-10 14:21:33 +00:00
Compare commits
164 Commits
Author | SHA1 | Date | |
---|---|---|---|
6cbcf23706 | |||
171da593c3 | |||
5b0d96e80e | |||
6fa39a1b75 | |||
480b79f588 | |||
7ec197cd5a | |||
81fdc0d267 | |||
c01c49d5e2 | |||
ceb640cca0 | |||
3712c16e08 | |||
4737493d26 | |||
bb95ef119c | |||
d6e1b96a09 | |||
071cdefd83 | |||
84d3471c01 | |||
64375d034d | |||
ca75c3ca4d | |||
0ff3a0a3cd | |||
edee20ba1a | |||
d793469311 | |||
0c869041b9 | |||
cb822757c1 | |||
ce86b7b4fb | |||
0c9cba3a5c | |||
066a157235 | |||
b917054acc | |||
a579ff818a | |||
6fa9dfc2f5 | |||
cad6d04295 | |||
215fa08cc8 | |||
834a15ddca | |||
33172f5850 | |||
6519e0ebd7 | |||
8341249aa6 | |||
4eed2700b0 | |||
d10166cdcb | |||
9faba46061 | |||
ad3fd30cf3 | |||
744061b7d2 | |||
aa80c86b0a | |||
0d53d93149 | |||
12e15ef236 | |||
d92bad28a0 | |||
a997237a2d | |||
bcc669028e | |||
896fe7ab0e | |||
e78f035a1c | |||
d32e52fb11 | |||
5393b25b22 | |||
665f7c6d66 | |||
03e02dfba9 | |||
019f84885d | |||
ceaae4c53f | |||
8e5d5c5694 | |||
53bb2d3e07 | |||
47f72296ed | |||
2d20a75114 | |||
8212c09088 | |||
c69254b00e | |||
4f36eda28f | |||
c53068c803 | |||
d771a12d86 | |||
3cd5cbb8ec | |||
09acdab0d3 | |||
54128c228c | |||
872dfb2c03 | |||
5f406c6ea0 | |||
8f785284dc | |||
ee0b1eaea5 | |||
1791c19c8c | |||
ea94a81a52 | |||
f3e705acc7 | |||
510b458de2 | |||
5aa74ee25d | |||
d991c475df | |||
40da1ec2b1 | |||
7789f5da19 | |||
a224b0bc54 | |||
9d958c3209 | |||
dc518f4178 | |||
1eacc5bc7b | |||
f6193301a5 | |||
8d792fe954 | |||
de506873e7 | |||
61340e3909 | |||
8e1413b984 | |||
163624c218 | |||
6fd8b076e2 | |||
e54ebb65fe | |||
9c8a8bb26b | |||
3f29ff5d33 | |||
a712fd6d22 | |||
7079f10bcc | |||
1210a9f613 | |||
5c76907f3d | |||
074e7e323b | |||
20994f5320 | |||
eac00292f2 | |||
bf768d3585 | |||
05f799f983 | |||
a81c328bf7 | |||
a6ba60a5c4 | |||
594b770d8e | |||
dbf0d2c422 | |||
275434f873 | |||
631dad8647 | |||
3eac0e0dd6 | |||
30d4bb641e | |||
b0aeff8f53 | |||
998c71fc84 | |||
b31245adc8 | |||
85a064765a | |||
fb56cc3c30 | |||
03d0c52d4d | |||
0aa7bb72e7 | |||
e9b3d3496f | |||
58e18dd01b | |||
fb017ebb07 | |||
08c4c169d6 | |||
de927e8052 | |||
df8e61632b | |||
b453bd4f83 | |||
0143ab6449 | |||
02dd32e7df | |||
4fe91796cd | |||
352876cade | |||
41b700f509 | |||
eea7e91b15 | |||
b11a7972f5 | |||
15d5bc53fb | |||
9d911af8e0 | |||
9f1f3c82dc | |||
d6a1f52962 | |||
7b536819b1 | |||
7158aaf702 | |||
bc87fad5f9 | |||
c9418399a7 | |||
2cac123405 | |||
ff47a9c228 | |||
f86a981eb2 | |||
674d68000b | |||
ae371085c1 | |||
770bee3c66 | |||
6943e3e90b | |||
a008ebd5b9 | |||
20108d2de8 | |||
15fcfb737c | |||
0fa14c9608 | |||
ac7c8a150e | |||
851c8ee2a3 | |||
7a3f9d08d5 | |||
52d60a7391 | |||
165068d05c | |||
9baae15dcf | |||
b87524f36a | |||
b0484c678e | |||
b6c498055f | |||
93fdedf67b | |||
9e3b6a80af | |||
997c275139 | |||
ba33f2ecd8 | |||
c74e2594f8 | |||
61757793ed | |||
01bd659ee8 |
19
.aegir.js
Normal file
19
.aegir.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
webpack: {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'node-forge': path.resolve(
|
||||||
|
path.dirname(require.resolve('libp2p-crypto')),
|
||||||
|
'../vendor/forge.bundle.js'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
'simple-websocket-server': '{}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
.npmignore
Normal file
35
.npmignore
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
test
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directory
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
@ -11,6 +11,7 @@ before_install:
|
|||||||
script:
|
script:
|
||||||
- npm run lint
|
- npm run lint
|
||||||
- npm test
|
- npm test
|
||||||
|
- npm run coverage
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
firefox: 'latest'
|
firefox: 'latest'
|
||||||
@ -18,3 +19,6 @@ addons:
|
|||||||
before_script:
|
before_script:
|
||||||
- export DISPLAY=:99.0
|
- export DISPLAY=:99.0
|
||||||
- sh -e /etc/init.d/xvfb start
|
- sh -e /etc/init.d/xvfb start
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- npm run coverage-publish
|
||||||
|
97
README.md
97
README.md
@ -1,25 +1,43 @@
|
|||||||
libp2p-swarm JavaScript implementation
|
libp2p-swarm JavaScript implementation
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
[](http://ipn.io)
|
[](http://ipn.io)
|
||||||
[](http://ipfs.io/)
|
[](http://ipfs.io/)
|
||||||
[](http://webchat.freenode.net/?channels=%23ipfs)
|
[](http://webchat.freenode.net/?channels=%23ipfs)
|
||||||
[](https://travis-ci.org/diasdavid/js-libp2p-swarm)
|
[](https://travis-ci.org/libp2p/js-libp2p-swarm)
|
||||||

|
[](https://coveralls.io/github/libp2p/js-libp2p-swarm?branch=master)
|
||||||
[](https://david-dm.org/ipfs/js-libp2p-swarm)
|
[](https://david-dm.org/libp2p/js-libp2p-swarm)
|
||||||
[](https://github.com/feross/standard)
|
[](https://github.com/feross/standard)
|
||||||
|
|
||||||
> libp2p swarm implementation in JavaScript
|
> libp2p swarm implementation in JavaScript.
|
||||||
|
|
||||||
# Description
|
libp2p-swarm is a connection abstraction that is able to leverage several transports and connection upgrades, such as congestion control, channel encryption, the multiplexing of 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.
|
||||||
|
|
||||||
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.
|
libp2p-swarm is used by [libp2p](https://github.com/libp2p/js-libp2p) but it can be also used as a standalone module.
|
||||||
|
|
||||||
libp2p-swarm is used by libp2p but it can be also used as a standalone module.
|
## Table of Contents
|
||||||
|
|
||||||
# Usage
|
- [Install](#install)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Create a libp2p Swarm](#create-a-libp2p-swarm)
|
||||||
|
- [API](#api)
|
||||||
|
- [Transports](#transports)
|
||||||
|
- [Connection](#connection)
|
||||||
|
- [`swarm.dial(pi, protocol, callback)`](#swarmdialpi-protocol-callback)
|
||||||
|
- [`swarm.hangUp(pi, callback)`](#swarmhanguppi-callback)
|
||||||
|
- [`swarm.listen(callback)`](#swarmlistencallback)
|
||||||
|
- [`swarm.handle(protocol, handler)`](#swarmhandleprotocol-handler)
|
||||||
|
- [`swarm.unhandle(protocol)`](#swarmunhandleprotocol)
|
||||||
|
- [`swarm.close(callback)`](#swarmclosecallback)
|
||||||
|
- [Design](#design)
|
||||||
|
- [Multitransport](#multitransport)
|
||||||
|
- [Connection upgrades](#connection-upgrades)
|
||||||
|
- [Identify](#identify)
|
||||||
|
- [Notes](#notes)
|
||||||
|
- [Contribute](#contribute)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
libp2p-swarm is available on npm and so, like any other npm module, just:
|
libp2p-swarm is available on npm and so, like any other npm module, just:
|
||||||
|
|
||||||
@ -27,9 +45,9 @@ libp2p-swarm is available on npm and so, like any other npm module, just:
|
|||||||
> npm install libp2p-swarm --save
|
> npm install libp2p-swarm --save
|
||||||
```
|
```
|
||||||
|
|
||||||
## API
|
## Usage
|
||||||
|
|
||||||
#### Create a libp2p Swarm
|
### Create a libp2p Swarm
|
||||||
|
|
||||||
And use it in your Node.js code as:
|
And use it in your Node.js code as:
|
||||||
|
|
||||||
@ -39,6 +57,8 @@ const Swarm = require('libp2p-swarm')
|
|||||||
const sw = new Swarm(peerInfo)
|
const sw = new Swarm(peerInfo)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
peerInfo is a [PeerInfo](https://github.com/diasdavid/js-peer-info) object that represents the peer creating this swarm instance.
|
peerInfo is a [PeerInfo](https://github.com/diasdavid/js-peer-info) object that represents the peer creating this swarm instance.
|
||||||
|
|
||||||
### Transports
|
### Transports
|
||||||
@ -47,10 +67,10 @@ peerInfo is a [PeerInfo](https://github.com/diasdavid/js-peer-info) object that
|
|||||||
|
|
||||||
libp2p-swarm expects transports that implement [interface-transport](https://github.com/diasdavid/abstract-transport). For example [libp2p-tcp](https://github.com/diasdavid/js-libp2p-tcp).
|
libp2p-swarm expects transports that implement [interface-transport](https://github.com/diasdavid/abstract-transport). For example [libp2p-tcp](https://github.com/diasdavid/js-libp2p-tcp).
|
||||||
|
|
||||||
- `key` - the transport identifier
|
- `key` - the transport identifier.
|
||||||
- `transport` -
|
- `transport` -
|
||||||
- `options`
|
- `options` -
|
||||||
- `callback`
|
- `callback` -
|
||||||
|
|
||||||
##### `swarm.transport.dial(key, multiaddrs, callback)`
|
##### `swarm.transport.dial(key, multiaddrs, callback)`
|
||||||
|
|
||||||
@ -92,38 +112,55 @@ Upgrading a connection to use a stream muxer is still considered an upgrade, but
|
|||||||
|
|
||||||
##### `swarm.connection.reuse()`
|
##### `swarm.connection.reuse()`
|
||||||
|
|
||||||
Enable the identify protocol
|
Enable the identify protocol.
|
||||||
|
|
||||||
### `swarm.dial(pi, protocol, callback)`
|
### `swarm.dial(pi, protocol, callback)`
|
||||||
|
|
||||||
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.
|
dial uses the best transport (whatever works first, in the future we can have some criteria), and jump starts the connection until the point where 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, then do nothing.
|
||||||
|
|
||||||
- `pi` - peer info project
|
- `pi` - peer info project
|
||||||
- `protocol`
|
- `protocol`
|
||||||
- `callback`
|
- `callback`
|
||||||
|
|
||||||
|
### `swarm.hangUp(pi, callback)`
|
||||||
|
|
||||||
|
Hang up the muxed connection we have with the peer.
|
||||||
|
|
||||||
|
- `pi` - peer info project
|
||||||
|
- `callback`
|
||||||
|
|
||||||
|
### `swarm.listen(callback)`
|
||||||
|
|
||||||
|
Start listening on all added transports that are available on the current `peerInfo`.
|
||||||
|
|
||||||
### `swarm.handle(protocol, handler)`
|
### `swarm.handle(protocol, handler)`
|
||||||
|
|
||||||
handle a new protocol.
|
Handle a new protocol.
|
||||||
|
|
||||||
- `protocol`
|
- `protocol`
|
||||||
- `handler` - function called when we receive a dial on `protocol. Signature must be `function (conn) {}`
|
- `handler` - function called when we receive a dial on `protocol. Signature must be `function (conn) {}`
|
||||||
|
|
||||||
|
### `swarm.unhandle(protocol)`
|
||||||
|
|
||||||
|
Unhandle a protocol.
|
||||||
|
|
||||||
|
- `protocol`
|
||||||
|
|
||||||
### `swarm.close(callback)`
|
### `swarm.close(callback)`
|
||||||
|
|
||||||
close all the listeners and muxers.
|
Close all the listeners and muxers.
|
||||||
|
|
||||||
- `callback`
|
- `callback`
|
||||||
|
|
||||||
# Design
|
## Design
|
||||||
|
|
||||||
## Multitransport
|
### Multitransport
|
||||||
|
|
||||||
libp2p is designed to support multiple transports at the same time. While peers are identified by their ID (which are generated from their public keys), the addresses of each pair may vary, depending the device where they are being run or the network in which they are accessible through.
|
libp2p is designed to support multiple transports at the same time. While peers are identified by their ID (which are generated from their public keys), the addresses of each pair may vary, depending the device where they are being run or the network in which they are accessible through.
|
||||||
|
|
||||||
In order for a transport to be supported, it has to follow the [interface-transport](https://github.com/diasdavid/interface-transport) spec.
|
In order for a transport to be supported, it has to follow the [interface-transport](https://github.com/diasdavid/interface-transport) spec.
|
||||||
|
|
||||||
## Connection upgrades
|
### Connection upgrades
|
||||||
|
|
||||||
Each connection in libp2p follows the [interface-connection](https://github.com/diasdavid/interface-connection) spec. This design decision enables libp2p to have upgradable transports.
|
Each connection in libp2p follows the [interface-connection](https://github.com/diasdavid/interface-connection) spec. This design decision enables libp2p to have upgradable transports.
|
||||||
|
|
||||||
@ -139,7 +176,7 @@ Types of upgrades to a connection:
|
|||||||
|
|
||||||
We also want to enable flexibility when it comes to upgrading a connection, for example, we might that all dialed transports pass through the encrypted channel upgrade, but not the congestion flow, specially when a transport might have already some underlying properties (UDP vs TCP vs WebRTC vs every other transport protocol)
|
We also want to enable flexibility when it comes to upgrading a connection, for example, we might that all dialed transports pass through the encrypted channel upgrade, but not the congestion flow, specially when a transport might have already some underlying properties (UDP vs TCP vs WebRTC vs every other transport protocol)
|
||||||
|
|
||||||
## Identify
|
### Identify
|
||||||
|
|
||||||
Identify is a protocol that Swarms mounts on top of itself, to identify the connections between any two peers. E.g:
|
Identify is a protocol that Swarms mounts on top of itself, to identify the connections between any two peers. E.g:
|
||||||
|
|
||||||
@ -150,9 +187,17 @@ Identify is a protocol that Swarms mounts on top of itself, to identify the conn
|
|||||||
|
|
||||||
In addition to this, we also share the 'observed addresses' by the other peer, which is extremely useful information for different kinds of network topologies.
|
In addition to this, we also share the 'observed addresses' by the other peer, which is extremely useful information for different kinds of network topologies.
|
||||||
|
|
||||||
## Notes
|
### Notes
|
||||||
|
|
||||||
To avoid the confusion between connection, stream, transport, and other names that represent an abstraction of data flow between two points, we use terms as:
|
To avoid the confusion between connection, stream, transport, and other names that represent an abstraction of data flow between two points, we use terms as:
|
||||||
|
|
||||||
- connection - something that implements the transversal expectations of a stream between two peers, including the benefits of using a stream plus having a way to do half duplex, full duplex
|
- connection - something that implements the transversal expectations of a stream between two peers, including the benefits of using a stream plus having a way to do half duplex, full duplex
|
||||||
- transport - something that as a dial/listen interface and return objs that implement a connection interface
|
- transport - something that as a dial/listen interface and return objs that implement a connection interface
|
||||||
|
|
||||||
|
## Contribute
|
||||||
|
|
||||||
|
This module is actively under development. Please check out the issues and submit PRs!
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT © Protocol Labs
|
||||||
|
12
circle.yml
Normal file
12
circle.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
machine:
|
||||||
|
node:
|
||||||
|
version: stable
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
pre:
|
||||||
|
- google-chrome --version
|
||||||
|
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||||||
|
- sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
|
||||||
|
- sudo apt-get update
|
||||||
|
- sudo apt-get --only-upgrade install google-chrome-stable
|
||||||
|
- google-chrome --version
|
46
gulpfile.js
46
gulpfile.js
@ -1,21 +1,31 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const gulp = require('gulp')
|
const gulp = require('gulp')
|
||||||
const Peer = require('peer-info')
|
const PeerInfo = require('peer-info')
|
||||||
const Id = require('peer-id')
|
const PeerId = require('peer-id')
|
||||||
const WebSockets = require('libp2p-websockets')
|
const WebSockets = require('libp2p-websockets')
|
||||||
|
|
||||||
const Swarm = require('./src')
|
const Swarm = require('./src')
|
||||||
|
const spdy = require('libp2p-spdy')
|
||||||
const multiaddr = require('multiaddr')
|
const multiaddr = require('multiaddr')
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const sigServer = require('libp2p-webrtc-star/src/signalling-server')
|
||||||
|
|
||||||
let swarmA
|
let swarmA
|
||||||
let swarmB
|
let swarmB
|
||||||
|
let sigS
|
||||||
|
|
||||||
gulp.task('test:browser:before', (done) => {
|
gulp.task('test:browser:before', (done) => {
|
||||||
function createListenerA (cb) {
|
function createListenerA (cb) {
|
||||||
const b58IdA = 'QmWg2L4Fucx1x4KXJTfKHGixBJvveubzcd7DdhB2Mqwfh1'
|
const id = PeerId.createFromJSON(
|
||||||
const peerA = new Peer(Id.createFromB58String(b58IdA))
|
JSON.parse(
|
||||||
const maA = multiaddr('/ip4/127.0.0.1/tcp/9100/websockets')
|
fs.readFileSync(
|
||||||
|
path.join(__dirname, './test/test-data/id-1.json'))))
|
||||||
|
|
||||||
|
const peerA = new PeerInfo(id)
|
||||||
|
const maA = multiaddr('/ip4/127.0.0.1/tcp/9100/ws')
|
||||||
|
|
||||||
peerA.multiaddr.add(maA)
|
peerA.multiaddr.add(maA)
|
||||||
swarmA = new Swarm(peerA)
|
swarmA = new Swarm(peerA)
|
||||||
@ -24,23 +34,30 @@ gulp.task('test:browser:before', (done) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createListenerB (cb) {
|
function createListenerB (cb) {
|
||||||
const b58IdB = 'QmRy1iU6BHmG5Hd8rnPhPL98cy1W1przUSTAMcGDq9yAAV'
|
const id = PeerId.createFromJSON(
|
||||||
const maB = multiaddr('/ip4/127.0.0.1/tcp/9200/websockets')
|
JSON.parse(
|
||||||
const peerB = new Peer(Id.createFromB58String(b58IdB))
|
fs.readFileSync(
|
||||||
|
path.join(__dirname, './test/test-data/id-2.json'))))
|
||||||
|
|
||||||
|
const peerB = new PeerInfo(id)
|
||||||
|
const maB = multiaddr('/ip4/127.0.0.1/tcp/9200/ws')
|
||||||
|
|
||||||
peerB.multiaddr.add(maB)
|
peerB.multiaddr.add(maB)
|
||||||
swarmB = new Swarm(peerB)
|
swarmB = new Swarm(peerB)
|
||||||
|
|
||||||
swarmB.transport.add('ws', new WebSockets())
|
swarmB.transport.add('ws', new WebSockets())
|
||||||
swarmB.transport.listen('ws', {}, null, cb)
|
swarmB.connection.addStreamMuxer(spdy)
|
||||||
|
swarmB.connection.reuse()
|
||||||
|
swarmB.listen(cb)
|
||||||
swarmB.handle('/echo/1.0.0', echo)
|
swarmB.handle('/echo/1.0.0', echo)
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = 0
|
let count = 0
|
||||||
const ready = () => ++count === 2 ? done() : null
|
const ready = () => ++count === 3 ? done() : null
|
||||||
|
|
||||||
createListenerA(ready)
|
createListenerA(ready)
|
||||||
createListenerB(ready)
|
createListenerB(ready)
|
||||||
|
sigS = sigServer.start(15555, ready)
|
||||||
|
|
||||||
function echo (conn) {
|
function echo (conn) {
|
||||||
conn.pipe(conn)
|
conn.pipe(conn)
|
||||||
@ -49,10 +66,11 @@ gulp.task('test:browser:before', (done) => {
|
|||||||
|
|
||||||
gulp.task('test:browser:after', (done) => {
|
gulp.task('test:browser:after', (done) => {
|
||||||
let count = 0
|
let count = 0
|
||||||
const ready = () => ++count === 2 ? done() : null
|
const ready = () => ++count === 3 ? done() : null
|
||||||
|
|
||||||
swarmA.transport.close('ws', ready)
|
swarmA.transport.close('ws', ready)
|
||||||
swarmB.transport.close('ws', ready)
|
swarmB.close(ready)
|
||||||
|
sigS.stop(ready)
|
||||||
})
|
})
|
||||||
|
|
||||||
require('dignified.js/gulp')(gulp)
|
require('aegir/gulp')(gulp)
|
||||||
|
76
package.json
76
package.json
@ -1,21 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "libp2p-swarm",
|
"name": "libp2p-swarm",
|
||||||
"version": "0.9.3",
|
"version": "0.22.3",
|
||||||
"description": "libp2p swarm implementation in JavaScript",
|
"description": "libp2p swarm implementation in JavaScript",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "dignified-lint",
|
"lint": "gulp lint",
|
||||||
"build": "dignified-build",
|
"build": "gulp build",
|
||||||
"test": "gulp test",
|
"test": "gulp test",
|
||||||
"test:node": "gulp test:node",
|
"test:node": "gulp test:node",
|
||||||
"test:browser": "gulp test:browser",
|
"test:browser": "gulp test:browser",
|
||||||
"release": "dignified-release",
|
"release": "gulp release",
|
||||||
"coverage": "istanbul cover --print both -- _mocha test/node.js"
|
"release-minor": "gulp release --type minor",
|
||||||
|
"release-major": "gulp release --type major",
|
||||||
|
"coverage": "gulp coverage",
|
||||||
|
"coverage-publish": "aegir-coverage publish"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/diasdavid/js-libp2p-swarm.git"
|
"url": "https://github.com/libp2p/js-libp2p-swarm.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"IPFS"
|
"IPFS"
|
||||||
@ -23,9 +26,9 @@
|
|||||||
"author": "David Dias <daviddias@ipfs.io>",
|
"author": "David Dias <daviddias@ipfs.io>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/diasdavid/js-libp2p-swarm/issues"
|
"url": "https://github.com/libp2p/js-libp2p-swarm/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/diasdavid/js-libp2p-swarm",
|
"homepage": "https://github.com/libp2p/js-libp2p-swarm",
|
||||||
"pre-commit": [
|
"pre-commit": [
|
||||||
"lint",
|
"lint",
|
||||||
"test"
|
"test"
|
||||||
@ -34,35 +37,44 @@
|
|||||||
"node": "^4.3.0"
|
"node": "^4.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bl": "^1.1.2",
|
"aegir": "^4.0.0",
|
||||||
"buffer-loader": "0.0.1",
|
"buffer-loader": "0.0.1",
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"dignified.js": "^1.0.0",
|
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"istanbul": "^0.4.2",
|
"istanbul": "^0.4.3",
|
||||||
"libp2p-multiplex": "^0.2.1",
|
"libp2p-multiplex": "^0.2.1",
|
||||||
"libp2p-spdy": "^0.2.3",
|
"libp2p-spdy": "^0.8.1",
|
||||||
"libp2p-tcp": "^0.4.0",
|
"libp2p-tcp": "^0.7.4",
|
||||||
"libp2p-websockets": "^0.2.1",
|
"libp2p-webrtc-star": "^0.3.2",
|
||||||
"multiaddr": "^1.3.0",
|
"libp2p-websockets": "^0.7.1",
|
||||||
"peer-id": "^0.6.0",
|
|
||||||
"peer-info": "^0.6.0",
|
|
||||||
"pre-commit": "^1.1.2",
|
"pre-commit": "^1.1.2",
|
||||||
"stream-pair": "^1.0.3"
|
"stream-pair": "^1.0.3",
|
||||||
|
"webrtcsupport": "^2.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"duplex-passthrough": "github:diasdavid/duplex-passthrough",
|
"babel-runtime": "^6.6.1",
|
||||||
"ip-address": "^5.0.2",
|
"bl": "^1.1.2",
|
||||||
"multistream-select": "^0.6.1",
|
"browserify-zlib": "github:ipfs/browserify-zlib",
|
||||||
"protocol-buffers-stream": "^1.2.0"
|
"debug": "^2.2.0",
|
||||||
|
"duplexify": "^3.4.3",
|
||||||
|
"interface-connection": "^0.1.7",
|
||||||
|
"ip-address": "^5.8.0",
|
||||||
|
"length-prefixed-stream": "^1.5.0",
|
||||||
|
"libp2p-identify": "^0.1.3",
|
||||||
|
"lodash.contains": "^2.4.3",
|
||||||
|
"multiaddr": "^2.0.0",
|
||||||
|
"multistream-select": "^0.9.0",
|
||||||
|
"peer-id": "^0.7.0",
|
||||||
|
"peer-info": "^0.7.0",
|
||||||
|
"protocol-buffers": "^3.1.6",
|
||||||
|
"run-parallel": "^1.1.6"
|
||||||
},
|
},
|
||||||
"dignified": {
|
"contributors": [
|
||||||
"webpack": {
|
"David Dias <daviddias.p@gmail.com>",
|
||||||
"resolve": {
|
"David Dias <mail@daviddias.me>",
|
||||||
"alias": {
|
"Francisco Baio Dias <xicombd@gmail.com>",
|
||||||
"node-forge": "../deps/forge.bundle.js"
|
"Pau Ramon Revilla <masylum@gmail.com>",
|
||||||
}
|
"Richard Littauer <richard.littauer@gmail.com>",
|
||||||
}
|
"dignifiedquire <dignifiedquire@gmail.com>"
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
}
|
|
74
src/connection.js
Normal file
74
src/connection.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const protocolMuxer = require('./protocol-muxer')
|
||||||
|
const identify = require('libp2p-identify')
|
||||||
|
const multistream = require('multistream-select')
|
||||||
|
|
||||||
|
module.exports = function connection (swarm) {
|
||||||
|
return {
|
||||||
|
addUpgrade () {},
|
||||||
|
|
||||||
|
addStreamMuxer (muxer) {
|
||||||
|
// for dialing
|
||||||
|
swarm.muxers[muxer.multicodec] = muxer
|
||||||
|
|
||||||
|
// for listening
|
||||||
|
swarm.handle(muxer.multicodec, (conn) => {
|
||||||
|
const muxedConn = muxer(conn, true)
|
||||||
|
|
||||||
|
muxedConn.on('stream', (conn) => {
|
||||||
|
protocolMuxer(swarm.protocols, conn)
|
||||||
|
})
|
||||||
|
|
||||||
|
// If identify is enabled
|
||||||
|
// 1. overload getPeerInfo
|
||||||
|
// 2. call getPeerInfo
|
||||||
|
// 3. add this conn to the pool
|
||||||
|
if (swarm.identify) {
|
||||||
|
// overload peerInfo to use Identify instead
|
||||||
|
conn.getPeerInfo = (cb) => {
|
||||||
|
const conn = muxedConn.newStream()
|
||||||
|
const ms = new multistream.Dialer()
|
||||||
|
ms.handle(conn, (err) => {
|
||||||
|
if (err) { return cb(err) }
|
||||||
|
|
||||||
|
ms.select(identify.multicodec, (err, conn) => {
|
||||||
|
if (err) { return cb(err) }
|
||||||
|
|
||||||
|
identify.exec(conn, (err, peerInfo, observedAddrs) => {
|
||||||
|
if (err) { return cb(err) }
|
||||||
|
|
||||||
|
observedAddrs.forEach((oa) => {
|
||||||
|
swarm._peerInfo.multiaddr.addSafe(oa)
|
||||||
|
})
|
||||||
|
|
||||||
|
cb(null, peerInfo)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.getPeerInfo((err, peerInfo) => {
|
||||||
|
if (err) {
|
||||||
|
return console.log('Identify not successful')
|
||||||
|
}
|
||||||
|
swarm.muxedConns[peerInfo.id.toB58String()] = {
|
||||||
|
muxer: muxedConn
|
||||||
|
}
|
||||||
|
|
||||||
|
swarm.emit('peer-mux-established', peerInfo)
|
||||||
|
muxedConn.on('close', () => {
|
||||||
|
delete swarm.muxedConns[peerInfo.id.toB58String()]
|
||||||
|
swarm.emit('peer-mux-closed', peerInfo)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
reuse () {
|
||||||
|
swarm.identify = true
|
||||||
|
swarm.handle(identify.multicodec, identify.handler(swarm._peerInfo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
185
src/dial.js
Normal file
185
src/dial.js
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const multistream = require('multistream-select')
|
||||||
|
const Connection = require('interface-connection').Connection
|
||||||
|
|
||||||
|
const protocolMuxer = require('./protocol-muxer')
|
||||||
|
|
||||||
|
module.exports = function dial (swarm) {
|
||||||
|
return (pi, protocol, callback) => {
|
||||||
|
if (typeof protocol === 'function') {
|
||||||
|
callback = protocol
|
||||||
|
protocol = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
callback = function noop () {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const proxyConn = new Connection()
|
||||||
|
|
||||||
|
const b58Id = pi.id.toB58String()
|
||||||
|
|
||||||
|
if (!swarm.muxedConns[b58Id]) {
|
||||||
|
if (!swarm.conns[b58Id]) {
|
||||||
|
attemptDial(pi, (err, conn) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err)
|
||||||
|
}
|
||||||
|
gotWarmedUpConn(conn)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const conn = swarm.conns[b58Id]
|
||||||
|
swarm.conns[b58Id] = undefined
|
||||||
|
gotWarmedUpConn(conn)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!protocol) {
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
gotMuxer(swarm.muxedConns[b58Id].muxer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxyConn
|
||||||
|
|
||||||
|
function gotWarmedUpConn (conn) {
|
||||||
|
conn.setPeerInfo(pi)
|
||||||
|
|
||||||
|
attemptMuxerUpgrade(conn, (err, muxer) => {
|
||||||
|
if (!protocol) {
|
||||||
|
if (err) {
|
||||||
|
swarm.conns[b58Id] = conn
|
||||||
|
}
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
// couldn't upgrade to Muxer, it is ok
|
||||||
|
protocolHandshake(conn, protocol, callback)
|
||||||
|
} else {
|
||||||
|
gotMuxer(muxer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotMuxer (muxer) {
|
||||||
|
if (swarm.identify) {
|
||||||
|
// TODO: Consider:
|
||||||
|
// 1. overload getPeerInfo
|
||||||
|
// 2. exec identify (through getPeerInfo)
|
||||||
|
// 3. update the peerInfo that is already stored in the conn
|
||||||
|
}
|
||||||
|
|
||||||
|
openConnInMuxedConn(muxer, (conn) => {
|
||||||
|
protocolHandshake(conn, protocol, callback)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function attemptDial (pi, cb) {
|
||||||
|
const tKeys = swarm.availableTransports(pi)
|
||||||
|
|
||||||
|
if (tKeys.length === 0) {
|
||||||
|
return cb(new Error('No available transport to dial to'))
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTransport(tKeys.shift())
|
||||||
|
|
||||||
|
function nextTransport (key) {
|
||||||
|
const multiaddrs = pi.multiaddrs.slice()
|
||||||
|
swarm.transport.dial(key, multiaddrs, (err, conn) => {
|
||||||
|
if (err) {
|
||||||
|
if (tKeys.length === 0) {
|
||||||
|
return cb(new Error('Could not dial in any of the transports'))
|
||||||
|
}
|
||||||
|
return nextTransport(tKeys.shift())
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptoDial()
|
||||||
|
|
||||||
|
function cryptoDial () {
|
||||||
|
// currently, no crypto channel is implemented
|
||||||
|
const ms = new multistream.Dialer()
|
||||||
|
ms.handle(conn, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return cb(err)
|
||||||
|
}
|
||||||
|
ms.select('/plaintext/1.0.0', cb)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function attemptMuxerUpgrade (conn, cb) {
|
||||||
|
const muxers = Object.keys(swarm.muxers)
|
||||||
|
if (muxers.length === 0) {
|
||||||
|
return cb(new Error('no muxers available'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. try to handshake in one of the muxers available
|
||||||
|
// 2. if succeeds
|
||||||
|
// - add the muxedConn to the list of muxedConns
|
||||||
|
// - add incomming new streams to connHandler
|
||||||
|
|
||||||
|
nextMuxer(muxers.shift())
|
||||||
|
|
||||||
|
function nextMuxer (key) {
|
||||||
|
const ms = new multistream.Dialer()
|
||||||
|
ms.handle(conn, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(new Error('multistream not supported'))
|
||||||
|
}
|
||||||
|
ms.select(key, (err, conn) => {
|
||||||
|
if (err) {
|
||||||
|
if (muxers.length === 0) {
|
||||||
|
cb(new Error('could not upgrade to stream muxing'))
|
||||||
|
} else {
|
||||||
|
nextMuxer(muxers.shift())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const muxedConn = swarm.muxers[key](conn, false)
|
||||||
|
swarm.muxedConns[b58Id] = {}
|
||||||
|
swarm.muxedConns[b58Id].muxer = muxedConn
|
||||||
|
// should not be needed anymore - swarm.muxedConns[b58Id].conn = conn
|
||||||
|
|
||||||
|
swarm.emit('peer-mux-established', pi)
|
||||||
|
|
||||||
|
muxedConn.once('close', () => {
|
||||||
|
delete swarm.muxedConns[pi.id.toB58String()]
|
||||||
|
swarm.emit('peer-mux-closed', pi)
|
||||||
|
})
|
||||||
|
|
||||||
|
// For incoming streams, in case identify is on
|
||||||
|
muxedConn.on('stream', (conn) => {
|
||||||
|
protocolMuxer(swarm.protocols, conn)
|
||||||
|
})
|
||||||
|
|
||||||
|
cb(null, muxedConn)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openConnInMuxedConn (muxer, cb) {
|
||||||
|
cb(muxer.newStream())
|
||||||
|
}
|
||||||
|
|
||||||
|
function protocolHandshake (conn, protocol, cb) {
|
||||||
|
const ms = new multistream.Dialer()
|
||||||
|
ms.handle(conn, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err)
|
||||||
|
}
|
||||||
|
ms.select(protocol, (err, conn) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err)
|
||||||
|
}
|
||||||
|
proxyConn.setInnerConn(conn)
|
||||||
|
callback(null, proxyConn)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
105
src/identify.js
105
src/identify.js
@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* Identify is one of the protocols swarms speaks in order to
|
|
||||||
* broadcast and learn about the ip:port pairs a specific peer
|
|
||||||
* is available through and to know when a new stream muxer is
|
|
||||||
* established, so a conn can be reused
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const multistream = require('multistream-select')
|
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const Info = require('peer-info')
|
|
||||||
const Id = require('peer-id')
|
|
||||||
const multiaddr = require('multiaddr')
|
|
||||||
|
|
||||||
const isNode = !global.window
|
|
||||||
|
|
||||||
const identity = isNode
|
|
||||||
? fs.readFileSync(path.join(__dirname, 'identify.proto'))
|
|
||||||
: require('buffer!./identify.proto')
|
|
||||||
|
|
||||||
const pbStream = require('protocol-buffers-stream')(identity)
|
|
||||||
|
|
||||||
exports = module.exports
|
|
||||||
exports.multicodec = '/ipfs/identify/1.0.0'
|
|
||||||
|
|
||||||
exports.exec = (rawConn, muxer, peerInfo, callback) => {
|
|
||||||
// 1. open a stream
|
|
||||||
// 2. multistream into identify
|
|
||||||
// 3. send what I see from this other peer (extract fro conn)
|
|
||||||
// 4. receive what the other peer sees from me
|
|
||||||
// 4. callback with (err, peerInfo)
|
|
||||||
|
|
||||||
const conn = muxer.newStream()
|
|
||||||
|
|
||||||
var msI = new multistream.Interactive()
|
|
||||||
msI.handle(conn, () => {
|
|
||||||
msI.select(exports.multicodec, (err, ds) => {
|
|
||||||
if (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var pbs = pbStream()
|
|
||||||
|
|
||||||
pbs.on('identify', (msg) => {
|
|
||||||
if (msg.observedAddr.length > 0) {
|
|
||||||
peerInfo.multiaddr.addSafe(msg.observedAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
const peerId = Id.createFromPubKey(msg.publicKey)
|
|
||||||
const otherPeerInfo = new Info(peerId)
|
|
||||||
msg.listenAddrs.forEach((ma) => {
|
|
||||||
otherPeerInfo.multiaddr.add(multiaddr(ma))
|
|
||||||
})
|
|
||||||
|
|
||||||
callback(null, otherPeerInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
const obsMultiaddr = rawConn.getObservedAddrs()[0]
|
|
||||||
|
|
||||||
pbs.identify({
|
|
||||||
protocolVersion: 'na',
|
|
||||||
agentVersion: 'na',
|
|
||||||
publicKey: peerInfo.id.pubKey,
|
|
||||||
listenAddrs: peerInfo.multiaddrs.map((mh) => { return mh.buffer }),
|
|
||||||
observedAddr: obsMultiaddr ? obsMultiaddr.buffer : new Buffer('')
|
|
||||||
})
|
|
||||||
|
|
||||||
pbs.pipe(ds).pipe(pbs)
|
|
||||||
pbs.finalize()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.handler = (peerInfo, swarm) => {
|
|
||||||
return function (conn) {
|
|
||||||
// 1. receive incoming observed info about me
|
|
||||||
// 2. update my own information (on peerInfo)
|
|
||||||
// 3. send back what I see from the other (get from swarm.muxedConns[incPeerID].conn.getObservedAddrs()
|
|
||||||
var pbs = pbStream()
|
|
||||||
|
|
||||||
pbs.on('identify', function (msg) {
|
|
||||||
if (msg.observedAddr.length > 0) {
|
|
||||||
peerInfo.multiaddr.addSafe(msg.observedAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
const peerId = Id.createFromPubKey(msg.publicKey)
|
|
||||||
const conn = swarm.muxedConns[peerId.toB58String()].conn
|
|
||||||
const obsMultiaddr = conn.getObservedAddrs()[0]
|
|
||||||
|
|
||||||
pbs.identify({
|
|
||||||
protocolVersion: 'na',
|
|
||||||
agentVersion: 'na',
|
|
||||||
publicKey: peerInfo.id.pubKey,
|
|
||||||
listenAddrs: peerInfo.multiaddrs.map(function (ma) {
|
|
||||||
return ma.buffer
|
|
||||||
}),
|
|
||||||
observedAddr: obsMultiaddr ? obsMultiaddr.buffer : new Buffer('')
|
|
||||||
})
|
|
||||||
pbs.finalize()
|
|
||||||
})
|
|
||||||
pbs.pipe(conn).pipe(pbs)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
message Identify {
|
|
||||||
|
|
||||||
// protocolVersion determines compatibility between peers
|
|
||||||
optional string protocolVersion = 5; // e.g. ipfs/1.0.0
|
|
||||||
|
|
||||||
// agentVersion is like a UserAgent string in browsers, or client version in bittorrent
|
|
||||||
// includes the client name and client.
|
|
||||||
optional string agentVersion = 6; // e.g. go-ipfs/0.1.0
|
|
||||||
|
|
||||||
// publicKey is this node's public key (which also gives its node.ID)
|
|
||||||
// - may not need to be sent, as secure channel implies it has been sent.
|
|
||||||
// - then again, if we change / disable secure channel, may still want it.
|
|
||||||
optional bytes publicKey = 1;
|
|
||||||
|
|
||||||
// listenAddrs are the multiaddrs the sender node listens for open connections on
|
|
||||||
repeated bytes listenAddrs = 2;
|
|
||||||
|
|
||||||
// oservedAddr is the multiaddr of the remote endpoint that the sender node perceives
|
|
||||||
// this is useful information to convey to the other side, as it helps the remote endpoint
|
|
||||||
// determine whether its connection to the local peer goes through NAT.
|
|
||||||
optional bytes observedAddr = 4;
|
|
||||||
|
|
||||||
// (DEPRECATED) protocols are the services this node is running
|
|
||||||
// repeated string protocols = 3;
|
|
||||||
}
|
|
348
src/index.js
348
src/index.js
@ -1,11 +1,19 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const multistream = require('multistream-select')
|
const util = require('util')
|
||||||
const identify = require('./identify')
|
const EE = require('events').EventEmitter
|
||||||
const DuplexPassThrough = require('duplex-passthrough')
|
const parallel = require('run-parallel')
|
||||||
|
const contains = require('lodash.contains')
|
||||||
|
|
||||||
|
const transport = require('./transport')
|
||||||
|
const connection = require('./connection')
|
||||||
|
const dial = require('./dial')
|
||||||
|
const protocolMuxer = require('./protocol-muxer')
|
||||||
|
|
||||||
exports = module.exports = Swarm
|
exports = module.exports = Swarm
|
||||||
|
|
||||||
|
util.inherits(Swarm, EE)
|
||||||
|
|
||||||
function Swarm (peerInfo) {
|
function Swarm (peerInfo) {
|
||||||
if (!(this instanceof Swarm)) {
|
if (!(this instanceof Swarm)) {
|
||||||
return new Swarm(peerInfo)
|
return new Swarm(peerInfo)
|
||||||
@ -15,99 +23,13 @@ function Swarm (peerInfo) {
|
|||||||
throw new Error('You must provide a value for `peerInfo`')
|
throw new Error('You must provide a value for `peerInfo`')
|
||||||
}
|
}
|
||||||
|
|
||||||
// transports --
|
this._peerInfo = peerInfo
|
||||||
|
|
||||||
|
// transports --
|
||||||
// { key: transport }; e.g { tcp: <tcp> }
|
// { key: transport }; e.g { tcp: <tcp> }
|
||||||
this.transports = {}
|
this.transports = {}
|
||||||
|
|
||||||
this.transport = {}
|
|
||||||
|
|
||||||
this.transport.add = (key, transport, options, callback) => {
|
|
||||||
if (typeof options === 'function') {
|
|
||||||
callback = options
|
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
if (!callback) { callback = noop }
|
|
||||||
|
|
||||||
if (this.transports[key]) {
|
|
||||||
throw new Error('There is already a transport with this key')
|
|
||||||
}
|
|
||||||
this.transports[key] = transport
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.transport.dial = (key, multiaddrs, callback) => {
|
|
||||||
const t = this.transports[key]
|
|
||||||
|
|
||||||
if (!Array.isArray(multiaddrs)) {
|
|
||||||
multiaddrs = [multiaddrs]
|
|
||||||
}
|
|
||||||
|
|
||||||
// a) filter the multiaddrs that are actually valid for this transport (use a func from the transport itself) (maybe even make the transport do that)
|
|
||||||
multiaddrs = t.filter(multiaddrs)
|
|
||||||
|
|
||||||
// b) if multiaddrs.length = 1, return the conn from the
|
|
||||||
// transport, otherwise, create a passthrough
|
|
||||||
if (multiaddrs.length === 1) {
|
|
||||||
const conn = t.dial(multiaddrs.shift(), {ready: () => {
|
|
||||||
const cb = callback
|
|
||||||
callback = noop // this is done to avoid connection drops as connect errors
|
|
||||||
cb(null, conn)
|
|
||||||
}})
|
|
||||||
conn.once('error', () => {
|
|
||||||
callback(new Error('failed to connect to every multiaddr'))
|
|
||||||
})
|
|
||||||
return conn
|
|
||||||
}
|
|
||||||
|
|
||||||
// c) multiaddrs should already be a filtered list
|
|
||||||
// specific for the transport we are using
|
|
||||||
const pt = new DuplexPassThrough()
|
|
||||||
|
|
||||||
next(multiaddrs.shift())
|
|
||||||
return pt
|
|
||||||
function next (multiaddr) {
|
|
||||||
const conn = t.dial(multiaddr, {ready: () => {
|
|
||||||
pt.wrapStream(conn)
|
|
||||||
const cb = callback
|
|
||||||
callback = noop // this is done to avoid connection drops as connect errors
|
|
||||||
cb(null, pt)
|
|
||||||
}})
|
|
||||||
|
|
||||||
conn.once('error', () => {
|
|
||||||
if (multiaddrs.length === 0) {
|
|
||||||
return callback(new Error('failed to connect to every multiaddr'))
|
|
||||||
}
|
|
||||||
next(multiaddrs.shift())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.transport.listen = (key, options, handler, callback) => {
|
|
||||||
// if no callback is passed, we pass conns to connHandler
|
|
||||||
if (!handler) { handler = connHandler }
|
|
||||||
|
|
||||||
const multiaddrs = this.transports[key].filter(peerInfo.multiaddrs)
|
|
||||||
|
|
||||||
this.transports[key].createListener(multiaddrs, handler, (err, maUpdate) => {
|
|
||||||
if (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
if (maUpdate) {
|
|
||||||
// because we can listen on port 0...
|
|
||||||
peerInfo.multiaddr.replace(multiaddrs, maUpdate)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.transport.close = (key, callback) => {
|
|
||||||
this.transports[key].close(callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
// connections --
|
// connections --
|
||||||
|
|
||||||
// { peerIdB58: { conn: <conn> }}
|
// { peerIdB58: { conn: <conn> }}
|
||||||
this.conns = {}
|
this.conns = {}
|
||||||
|
|
||||||
@ -122,204 +44,92 @@ function Swarm (peerInfo) {
|
|||||||
// { protocol: handler }
|
// { protocol: handler }
|
||||||
this.protocols = {}
|
this.protocols = {}
|
||||||
|
|
||||||
this.connection = {}
|
|
||||||
this.connection.addUpgrade = () => {}
|
|
||||||
|
|
||||||
// { muxerCodec: <muxer> } e.g { '/spdy/0.3.1': spdy }
|
// { muxerCodec: <muxer> } e.g { '/spdy/0.3.1': spdy }
|
||||||
this.muxers = {}
|
this.muxers = {}
|
||||||
this.connection.addStreamMuxer = (muxer) => {
|
|
||||||
// for dialing
|
|
||||||
this.muxers[muxer.multicodec] = muxer
|
|
||||||
|
|
||||||
// for listening
|
// is the Identify protocol enabled?
|
||||||
this.handle(muxer.multicodec, (conn) => {
|
this.identify = false
|
||||||
const muxedConn = muxer(conn, true)
|
|
||||||
muxedConn.on('stream', (conn) => {
|
this.transport = transport(this)
|
||||||
connHandler(conn)
|
this.connection = connection(this)
|
||||||
|
|
||||||
|
this.availableTransports = (pi) => {
|
||||||
|
const addrs = pi.multiaddrs
|
||||||
|
|
||||||
|
// Only listen on transports we actually have addresses for
|
||||||
|
return Object.keys(this.transports).filter((ts) => {
|
||||||
|
// ipfs multiaddrs are not dialable so we drop them here
|
||||||
|
let dialable = addrs.map((addr) => {
|
||||||
|
// webrtc-star needs the /ipfs/QmHash
|
||||||
|
if (addr.toString().indexOf('webrtc-star') > 0) {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contains(addr.protoNames(), 'ipfs')) {
|
||||||
|
return addr.decapsulate('ipfs')
|
||||||
|
}
|
||||||
|
return addr
|
||||||
})
|
})
|
||||||
|
|
||||||
// if identify is enabled, attempt to do it for muxer reuse
|
return this.transports[ts].filter(dialable).length > 0
|
||||||
if (this.identify) {
|
|
||||||
identify.exec(conn, muxedConn, peerInfo, (err, pi) => {
|
|
||||||
if (err) {
|
|
||||||
return console.log('Identify exec failed', err)
|
|
||||||
}
|
|
||||||
this.muxedConns[pi.id.toB58String()] = {}
|
|
||||||
this.muxedConns[pi.id.toB58String()].muxer = muxedConn
|
|
||||||
this.muxedConns[pi.id.toB58String()].conn = conn // to be able to extract addrs
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable the Identify protocol
|
|
||||||
this.identify = false
|
|
||||||
this.connection.reuse = () => {
|
|
||||||
this.identify = true
|
|
||||||
this.handle(identify.multicodec, identify.handler(peerInfo, this))
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = this // prefered this to bind
|
|
||||||
|
|
||||||
// incomming connection handler
|
|
||||||
function connHandler (conn) {
|
|
||||||
var msS = new multistream.Select()
|
|
||||||
Object.keys(self.protocols).forEach((protocol) => {
|
|
||||||
if (!protocol) { return }
|
|
||||||
msS.addHandler(protocol, self.protocols[protocol])
|
|
||||||
})
|
|
||||||
msS.handle(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// higher level (public) API
|
// higher level (public) API
|
||||||
this.dial = (pi, protocol, callback) => {
|
this.dial = dial(this)
|
||||||
var pt = null
|
|
||||||
if (typeof protocol === 'function') {
|
|
||||||
callback = protocol
|
|
||||||
protocol = null
|
|
||||||
} else {
|
|
||||||
pt = new DuplexPassThrough()
|
|
||||||
}
|
|
||||||
|
|
||||||
const b58Id = pi.id.toB58String()
|
// Start listening on all available transports
|
||||||
if (!this.muxedConns[b58Id]) {
|
this.listen = (callback) => {
|
||||||
if (!this.conns[b58Id]) {
|
parallel(this.availableTransports(peerInfo).map((ts) => (cb) => {
|
||||||
attemptDial(pi, (err, conn) => {
|
// Listen on the given transport
|
||||||
if (err) {
|
this.transport.listen(ts, {}, null, cb)
|
||||||
return callback(err)
|
}), callback)
|
||||||
}
|
|
||||||
gotWarmedUpConn(conn)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
const conn = this.conns[b58Id]
|
|
||||||
this.conns[b58Id] = undefined
|
|
||||||
gotWarmedUpConn(conn)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gotMuxer(this.muxedConns[b58Id].muxer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pt
|
|
||||||
|
|
||||||
function gotWarmedUpConn (conn) {
|
|
||||||
attemptMuxerUpgrade(conn, (err, muxer) => {
|
|
||||||
if (!protocol) {
|
|
||||||
if (err) {
|
|
||||||
self.conns[b58Id] = conn
|
|
||||||
}
|
|
||||||
return callback()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
// couldn't upgrade to Muxer, it is ok
|
|
||||||
protocolHandshake(conn, protocol, callback)
|
|
||||||
} else {
|
|
||||||
gotMuxer(muxer)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function gotMuxer (muxer) {
|
|
||||||
openConnInMuxedConn(muxer, (conn) => {
|
|
||||||
protocolHandshake(conn, protocol, callback)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function attemptDial (pi, cb) {
|
|
||||||
const tKeys = Object.keys(self.transports)
|
|
||||||
nextTransport(tKeys.shift())
|
|
||||||
|
|
||||||
function nextTransport (key) {
|
|
||||||
const multiaddrs = pi.multiaddrs.slice()
|
|
||||||
self.transport.dial(key, multiaddrs, (err, conn) => {
|
|
||||||
if (err) {
|
|
||||||
if (tKeys.length === 0) {
|
|
||||||
return cb(new Error('Could not dial in any of the transports'))
|
|
||||||
}
|
|
||||||
return nextTransport(tKeys.shift())
|
|
||||||
}
|
|
||||||
cb(null, conn)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function attemptMuxerUpgrade (conn, cb) {
|
|
||||||
const muxers = Object.keys(self.muxers)
|
|
||||||
if (muxers.length === 0) {
|
|
||||||
return cb(new Error('no muxers available'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. try to handshake in one of the muxers available
|
|
||||||
// 2. if succeeds
|
|
||||||
// - add the muxedConn to the list of muxedConns
|
|
||||||
// - add incomming new streams to connHandler
|
|
||||||
|
|
||||||
nextMuxer(muxers.shift())
|
|
||||||
|
|
||||||
function nextMuxer (key) {
|
|
||||||
var msI = new multistream.Interactive()
|
|
||||||
msI.handle(conn, function () {
|
|
||||||
msI.select(key, (err, conn) => {
|
|
||||||
if (err) {
|
|
||||||
if (muxers.length === 0) {
|
|
||||||
cb(new Error('could not upgrade to stream muxing'))
|
|
||||||
} else {
|
|
||||||
nextMuxer(muxers.shift())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const muxedConn = self.muxers[key](conn, false)
|
|
||||||
self.muxedConns[b58Id] = {}
|
|
||||||
self.muxedConns[b58Id].muxer = muxedConn
|
|
||||||
self.muxedConns[b58Id].conn = conn
|
|
||||||
|
|
||||||
// in case identify is on
|
|
||||||
muxedConn.on('stream', connHandler)
|
|
||||||
|
|
||||||
cb(null, muxedConn)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function openConnInMuxedConn (muxer, cb) {
|
|
||||||
cb(muxer.newStream())
|
|
||||||
}
|
|
||||||
|
|
||||||
function protocolHandshake (conn, protocol, cb) {
|
|
||||||
var msI = new multistream.Interactive()
|
|
||||||
msI.handle(conn, function () {
|
|
||||||
msI.select(protocol, (err, conn) => {
|
|
||||||
if (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pt.wrapStream(conn)
|
|
||||||
callback(null, pt)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handle = (protocol, handler) => {
|
this.handle = (protocol, handler) => {
|
||||||
this.protocols[protocol] = handler
|
this.protocols[protocol] = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
this.close = (callback) => {
|
// our crypto handshake :)
|
||||||
var count = 0
|
this.handle('/plaintext/1.0.0', (conn) => {
|
||||||
|
protocolMuxer(this.protocols, conn)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.unhandle = (protocol, handler) => {
|
||||||
|
if (this.protocols[protocol]) {
|
||||||
|
delete this.protocols[protocol]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hangUp = (peerInfo, callback) => {
|
||||||
|
const key = peerInfo.id.toB58String()
|
||||||
|
if (this.muxedConns[key]) {
|
||||||
|
const muxer = this.muxedConns[key].muxer
|
||||||
|
muxer.end()
|
||||||
|
muxer.once('close', () => {
|
||||||
|
delete this.muxedConns[key]
|
||||||
|
callback()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.close = (callback) => {
|
||||||
Object.keys(this.muxedConns).forEach((key) => {
|
Object.keys(this.muxedConns).forEach((key) => {
|
||||||
this.muxedConns[key].muxer.end()
|
this.muxedConns[key].muxer.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.keys(this.transports).forEach((key) => {
|
const transports = this.transports
|
||||||
this.transports[key].close(() => {
|
|
||||||
if (++count === Object.keys(this.transports).length) {
|
parallel(Object.keys(transports).map((key) => {
|
||||||
callback()
|
return (cb) => {
|
||||||
}
|
parallel(transports[key].listeners.map((listener) => {
|
||||||
})
|
return (cb) => {
|
||||||
})
|
listener.close(cb)
|
||||||
|
}
|
||||||
|
}), cb)
|
||||||
|
}
|
||||||
|
}), callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function noop () {}
|
|
||||||
|
21
src/protocol-muxer.js
Normal file
21
src/protocol-muxer.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const multistream = require('multistream-select')
|
||||||
|
|
||||||
|
module.exports = function protocolMuxer (protocols, conn) {
|
||||||
|
const ms = new multistream.Listener()
|
||||||
|
|
||||||
|
Object.keys(protocols).forEach((protocol) => {
|
||||||
|
if (!protocol) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ms.addHandler(protocol, protocols[protocol])
|
||||||
|
})
|
||||||
|
|
||||||
|
ms.handle(conn, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return // the multistream handshake failed
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
154
src/transport.js
Normal file
154
src/transport.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Connection = require('interface-connection').Connection
|
||||||
|
const parallel = require('run-parallel')
|
||||||
|
const debug = require('debug')
|
||||||
|
const log = debug('libp2p:swarm')
|
||||||
|
|
||||||
|
const protocolMuxer = require('./protocol-muxer')
|
||||||
|
|
||||||
|
module.exports = function (swarm) {
|
||||||
|
return {
|
||||||
|
add (key, transport, options, callback) {
|
||||||
|
if (typeof options === 'function') {
|
||||||
|
callback = options
|
||||||
|
options = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!callback) { callback = noop }
|
||||||
|
|
||||||
|
if (swarm.transports[key]) {
|
||||||
|
throw new Error('There is already a transport with this key')
|
||||||
|
}
|
||||||
|
swarm.transports[key] = transport
|
||||||
|
if (!swarm.transports[key].listeners) {
|
||||||
|
swarm.transports[key].listeners = []
|
||||||
|
}
|
||||||
|
|
||||||
|
callback()
|
||||||
|
},
|
||||||
|
|
||||||
|
dial (key, multiaddrs, callback) {
|
||||||
|
const t = swarm.transports[key]
|
||||||
|
|
||||||
|
if (!Array.isArray(multiaddrs)) {
|
||||||
|
multiaddrs = [multiaddrs]
|
||||||
|
}
|
||||||
|
|
||||||
|
// a) filter the multiaddrs that are actually valid for this transport (use a func from the transport itself) (maybe even make the transport do that)
|
||||||
|
multiaddrs = dialables(t, multiaddrs)
|
||||||
|
|
||||||
|
// b) if multiaddrs.length = 1, return the conn from the
|
||||||
|
// transport, otherwise, create a passthrough
|
||||||
|
if (multiaddrs.length === 1) {
|
||||||
|
const conn = t.dial(multiaddrs.shift())
|
||||||
|
|
||||||
|
conn.once('error', connectError)
|
||||||
|
|
||||||
|
conn.once('connect', () => {
|
||||||
|
conn.removeListener('error', connectError)
|
||||||
|
callback(null, conn)
|
||||||
|
})
|
||||||
|
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
function connectError () {
|
||||||
|
callback(new Error('failed to connect to every multiaddr'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// c) multiaddrs should already be a filtered list
|
||||||
|
// specific for the transport we are using
|
||||||
|
const proxyConn = new Connection()
|
||||||
|
|
||||||
|
next(multiaddrs.shift())
|
||||||
|
|
||||||
|
return proxyConn
|
||||||
|
|
||||||
|
// TODO improve in the future to make all the dials in paralell
|
||||||
|
function next (multiaddr) {
|
||||||
|
const conn = t.dial(multiaddr)
|
||||||
|
|
||||||
|
conn.once('error', connectError)
|
||||||
|
|
||||||
|
function connectError () {
|
||||||
|
if (multiaddrs.length === 0) {
|
||||||
|
return callback(new Error('failed to connect to every multiaddr'))
|
||||||
|
}
|
||||||
|
next(multiaddrs.shift())
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.once('connect', () => {
|
||||||
|
conn.removeListener('error', connectError)
|
||||||
|
proxyConn.setInnerConn(conn)
|
||||||
|
callback(null, proxyConn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
listen (key, options, handler, callback) {
|
||||||
|
// if no handler is passed, we pass conns to protocolMuxer
|
||||||
|
if (!handler) {
|
||||||
|
handler = protocolMuxer.bind(null, swarm.protocols)
|
||||||
|
}
|
||||||
|
|
||||||
|
const multiaddrs = dialables(swarm.transports[key], swarm._peerInfo.multiaddrs)
|
||||||
|
|
||||||
|
const transport = swarm.transports[key]
|
||||||
|
|
||||||
|
if (!transport.listeners) {
|
||||||
|
transport.listeners = []
|
||||||
|
}
|
||||||
|
|
||||||
|
let freshMultiaddrs = []
|
||||||
|
|
||||||
|
const createListeners = multiaddrs.map((ma) => {
|
||||||
|
return (cb) => {
|
||||||
|
const listener = transport.createListener(handler)
|
||||||
|
listener.listen(ma, () => {
|
||||||
|
log('Listener started on:', ma.toString())
|
||||||
|
listener.getAddrs((err, addrs) => {
|
||||||
|
if (err) {
|
||||||
|
return cb(err)
|
||||||
|
}
|
||||||
|
freshMultiaddrs = freshMultiaddrs.concat(addrs)
|
||||||
|
transport.listeners.push(listener)
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
parallel(createListeners, () => {
|
||||||
|
// cause we can listen on port 0 or 0.0.0.0
|
||||||
|
swarm._peerInfo.multiaddr.replace(multiaddrs, freshMultiaddrs)
|
||||||
|
callback()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
close (key, callback) {
|
||||||
|
const transport = swarm.transports[key]
|
||||||
|
|
||||||
|
if (!transport) {
|
||||||
|
return callback(new Error(`Trying to close non existing transport: ${key}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
parallel(transport.listeners.map((listener) => {
|
||||||
|
return (cb) => {
|
||||||
|
listener.close(cb)
|
||||||
|
}
|
||||||
|
}), callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dialables (tp, multiaddrs) {
|
||||||
|
return tp.filter(multiaddrs.map((addr) => {
|
||||||
|
// webrtc-star needs the /ipfs/QmHash
|
||||||
|
if (addr.toString().indexOf('webrtc-star') > 0) {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
function noop () {}
|
@ -1,13 +0,0 @@
|
|||||||
/* eslint-env mocha */
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const expect = require('chai').expect
|
|
||||||
|
|
||||||
const Swarm = require('../src')
|
|
||||||
|
|
||||||
describe('basics', () => {
|
|
||||||
it('throws on missing peerInfo', (done) => {
|
|
||||||
expect(Swarm).to.throw(Error)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
12
test/00-instance.node.js
Normal file
12
test/00-instance.node.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/* eslint-env mocha */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const expect = require('chai').expect
|
||||||
|
|
||||||
|
const Swarm = require('../src')
|
||||||
|
|
||||||
|
describe('create Swarm instance', () => {
|
||||||
|
it('throws on missing peerInfo', () => {
|
||||||
|
expect(() => Swarm()).to.throw(Error)
|
||||||
|
})
|
||||||
|
})
|
@ -1,8 +1,10 @@
|
|||||||
/* eslint-env mocha */
|
/* eslint-env mocha */
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const expect = require('chai').expect
|
const expect = require('chai').expect
|
||||||
|
|
||||||
|
const parallel = require('run-parallel')
|
||||||
const multiaddr = require('multiaddr')
|
const multiaddr = require('multiaddr')
|
||||||
const Peer = require('peer-info')
|
const Peer = require('peer-info')
|
||||||
const Swarm = require('../src')
|
const Swarm = require('../src')
|
||||||
@ -46,9 +48,17 @@ describe('transport - tcp', function () {
|
|||||||
function ready () {
|
function ready () {
|
||||||
if (++count === 2) {
|
if (++count === 2) {
|
||||||
expect(peerA.multiaddrs.length).to.equal(1)
|
expect(peerA.multiaddrs.length).to.equal(1)
|
||||||
expect(peerA.multiaddrs[0]).to.deep.equal(multiaddr('/ip4/127.0.0.1/tcp/9888'))
|
expect(
|
||||||
|
peerA.multiaddrs[0].equals(multiaddr('/ip4/127.0.0.1/tcp/9888'))
|
||||||
|
).to.be.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(peerB.multiaddrs.length).to.equal(1)
|
expect(peerB.multiaddrs.length).to.equal(1)
|
||||||
expect(peerB.multiaddrs[0]).to.deep.equal(multiaddr('/ip4/127.0.0.1/tcp/9999'))
|
expect(
|
||||||
|
peerB.multiaddrs[0].equals(multiaddr('/ip4/127.0.0.1/tcp/9999'))
|
||||||
|
).to.be.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,7 +78,7 @@ describe('transport - tcp', function () {
|
|||||||
|
|
||||||
it('dial to set of multiaddr, only one is available', (done) => {
|
it('dial to set of multiaddr, only one is available', (done) => {
|
||||||
const conn = swarmA.transport.dial('tcp', [
|
const conn = swarmA.transport.dial('tcp', [
|
||||||
multiaddr('/ip4/127.0.0.1/tcp/9910/websockets'), // not valid on purpose
|
multiaddr('/ip4/127.0.0.1/tcp/9910/ws'), // not valid on purpose
|
||||||
multiaddr('/ip4/127.0.0.1/tcp/9910'),
|
multiaddr('/ip4/127.0.0.1/tcp/9910'),
|
||||||
multiaddr('/ip4/127.0.0.1/tcp/9999'),
|
multiaddr('/ip4/127.0.0.1/tcp/9999'),
|
||||||
multiaddr('/ip4/127.0.0.1/tcp/9309')
|
multiaddr('/ip4/127.0.0.1/tcp/9309')
|
||||||
@ -84,15 +94,10 @@ describe('transport - tcp', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('close', (done) => {
|
it('close', (done) => {
|
||||||
var count = 0
|
parallel([
|
||||||
swarmA.transport.close('tcp', closed)
|
(cb) => swarmA.transport.close('tcp', cb),
|
||||||
swarmB.transport.close('tcp', closed)
|
(cb) => swarmB.transport.close('tcp', cb)
|
||||||
|
], done)
|
||||||
function closed () {
|
|
||||||
if (++count === 2) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('support port 0', (done) => {
|
it('support port 0', (done) => {
|
||||||
@ -123,8 +128,12 @@ describe('transport - tcp', function () {
|
|||||||
}, ready)
|
}, ready)
|
||||||
|
|
||||||
function ready () {
|
function ready () {
|
||||||
expect(peer.multiaddrs.length).to.equal(1)
|
expect(peer.multiaddrs.length >= 1).to.equal(true)
|
||||||
expect(peer.multiaddrs[0]).to.deep.equal(multiaddr('/ip4/0.0.0.0/tcp/9050'))
|
expect(
|
||||||
|
peer.multiaddrs[0].equals(multiaddr('/ip4/127.0.0.1/tcp/9050'))
|
||||||
|
).to.be.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
swarm.close(done)
|
swarm.close(done)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -140,7 +149,7 @@ describe('transport - tcp', function () {
|
|||||||
}, ready)
|
}, ready)
|
||||||
|
|
||||||
function ready () {
|
function ready () {
|
||||||
expect(peer.multiaddrs.length).to.equal(1)
|
expect(peer.multiaddrs.length >= 1).to.equal(true)
|
||||||
expect(peer.multiaddrs[0]).to.not.deep.equal(multiaddr('/ip4/0.0.0.0/tcp/0'))
|
expect(peer.multiaddrs[0]).to.not.deep.equal(multiaddr('/ip4/0.0.0.0/tcp/0'))
|
||||||
swarm.close(done)
|
swarm.close(done)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
const expect = require('chai').expect
|
const expect = require('chai').expect
|
||||||
|
|
||||||
|
const parallel = require('run-parallel')
|
||||||
const multiaddr = require('multiaddr')
|
const multiaddr = require('multiaddr')
|
||||||
const Peer = require('peer-info')
|
const Peer = require('peer-info')
|
||||||
const Swarm = require('../src')
|
const Swarm = require('../src')
|
||||||
@ -17,12 +18,11 @@ describe('transport - websockets', function () {
|
|||||||
var peerA = new Peer()
|
var peerA = new Peer()
|
||||||
var peerB = new Peer()
|
var peerB = new Peer()
|
||||||
|
|
||||||
before((done) => {
|
before(() => {
|
||||||
peerA.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9888/websockets'))
|
peerA.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9888/ws'))
|
||||||
peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9999/websockets'))
|
peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9999/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC'))
|
||||||
swarmA = new Swarm(peerA)
|
swarmA = new Swarm(peerA)
|
||||||
swarmB = new Swarm(peerB)
|
swarmB = new Swarm(peerB)
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('add', (done) => {
|
it('add', (done) => {
|
||||||
@ -35,27 +35,32 @@ describe('transport - websockets', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('listen', (done) => {
|
it('listen', (done) => {
|
||||||
var count = 0
|
parallel([
|
||||||
swarmA.transport.listen('ws', {}, (conn) => {
|
(cb) => swarmA.transport.listen('ws', {}, (conn) => {
|
||||||
conn.pipe(conn)
|
conn.pipe(conn)
|
||||||
}, ready)
|
}, cb),
|
||||||
swarmB.transport.listen('ws', {}, (conn) => {
|
(cb) => swarmB.transport.listen('ws', {}, (conn) => {
|
||||||
conn.pipe(conn)
|
conn.pipe(conn)
|
||||||
}, ready)
|
}, cb)
|
||||||
|
], () => {
|
||||||
function ready () {
|
expect(peerA.multiaddrs.length).to.equal(1)
|
||||||
if (++count === 2) {
|
expect(
|
||||||
expect(peerA.multiaddrs.length).to.equal(1)
|
peerA.multiaddrs[0].equals(multiaddr('/ip4/127.0.0.1/tcp/9888/ws'))
|
||||||
expect(peerA.multiaddrs[0]).to.deep.equal(multiaddr('/ip4/127.0.0.1/tcp/9888/websockets'))
|
).to.be.equal(
|
||||||
expect(peerB.multiaddrs.length).to.equal(1)
|
true
|
||||||
expect(peerB.multiaddrs[0]).to.deep.equal(multiaddr('/ip4/127.0.0.1/tcp/9999/websockets'))
|
)
|
||||||
done()
|
expect(peerB.multiaddrs.length).to.equal(1)
|
||||||
}
|
expect(
|
||||||
}
|
peerB.multiaddrs[0].equals(multiaddr('/ip4/127.0.0.1/tcp/9999/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC'))
|
||||||
|
).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
|
done()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('dial', (done) => {
|
it('dial', (done) => {
|
||||||
const conn = swarmA.transport.dial('ws', multiaddr('/ip4/127.0.0.1/tcp/9999/websockets'), (err, conn) => {
|
const conn = swarmA.transport.dial('ws', multiaddr('/ip4/127.0.0.1/tcp/9999/ws'), (err, conn) => {
|
||||||
expect(err).to.not.exist
|
expect(err).to.not.exist
|
||||||
})
|
})
|
||||||
conn.pipe(bl((err, data) => {
|
conn.pipe(bl((err, data) => {
|
||||||
@ -67,7 +72,7 @@ describe('transport - websockets', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('dial (conn from callback)', (done) => {
|
it('dial (conn from callback)', (done) => {
|
||||||
swarmA.transport.dial('ws', multiaddr('/ip4/127.0.0.1/tcp/9999/websockets'), (err, conn) => {
|
swarmA.transport.dial('ws', multiaddr('/ip4/127.0.0.1/tcp/9999/ws'), (err, conn) => {
|
||||||
expect(err).to.not.exist
|
expect(err).to.not.exist
|
||||||
|
|
||||||
conn.pipe(bl((err, data) => {
|
conn.pipe(bl((err, data) => {
|
||||||
@ -80,14 +85,9 @@ describe('transport - websockets', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('close', (done) => {
|
it('close', (done) => {
|
||||||
var count = 0
|
parallel([
|
||||||
swarmA.transport.close('ws', closed)
|
(cb) => swarmA.transport.close('ws', cb),
|
||||||
swarmB.transport.close('ws', closed)
|
(cb) => swarmB.transport.close('ws', cb)
|
||||||
|
], done)
|
||||||
function closed () {
|
|
||||||
if (++count === 2) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -3,14 +3,16 @@
|
|||||||
|
|
||||||
const expect = require('chai').expect
|
const expect = require('chai').expect
|
||||||
|
|
||||||
|
const parallel = require('run-parallel')
|
||||||
const multiaddr = require('multiaddr')
|
const multiaddr = require('multiaddr')
|
||||||
const Peer = require('peer-info')
|
const Peer = require('peer-info')
|
||||||
const Swarm = require('../src')
|
const Swarm = require('../src')
|
||||||
const TCP = require('libp2p-tcp')
|
const TCP = require('libp2p-tcp')
|
||||||
const multiplex = require('libp2p-spdy')
|
const multiplex = require('libp2p-spdy')
|
||||||
|
|
||||||
describe('stream muxing with multiplex (on TCP)', function () {
|
// TODO multiplex needs to be upgraded, like spdy, to work again
|
||||||
this.timeout(20000)
|
describe.skip('stream muxing with multiplex (on TCP)', function () {
|
||||||
|
this.timeout(60 * 1000)
|
||||||
|
|
||||||
var swarmA
|
var swarmA
|
||||||
var peerA
|
var peerA
|
||||||
@ -37,35 +39,22 @@ describe('stream muxing with multiplex (on TCP)', function () {
|
|||||||
swarmC = new Swarm(peerC)
|
swarmC = new Swarm(peerC)
|
||||||
|
|
||||||
swarmA.transport.add('tcp', new TCP())
|
swarmA.transport.add('tcp', new TCP())
|
||||||
swarmA.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
swarmB.transport.add('tcp', new TCP())
|
swarmB.transport.add('tcp', new TCP())
|
||||||
swarmB.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
swarmC.transport.add('tcp', new TCP())
|
swarmC.transport.add('tcp', new TCP())
|
||||||
swarmC.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmA.transport.listen('tcp', {}, null, cb),
|
||||||
function ready () {
|
(cb) => swarmB.transport.listen('tcp', {}, null, cb),
|
||||||
if (++counter === 3) {
|
(cb) => swarmC.transport.listen('tcp', {}, null, cb)
|
||||||
done()
|
], done)
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
after((done) => {
|
after((done) => {
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmA.close(cb),
|
||||||
swarmA.close(closed)
|
(cb) => swarmB.close(cb),
|
||||||
swarmB.close(closed)
|
(cb) => swarmC.close(cb)
|
||||||
swarmC.close(closed)
|
], done)
|
||||||
|
|
||||||
function closed () {
|
|
||||||
if (++counter === 3) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('add', (done) => {
|
it('add', (done) => {
|
||||||
|
@ -3,14 +3,17 @@
|
|||||||
|
|
||||||
const expect = require('chai').expect
|
const expect = require('chai').expect
|
||||||
|
|
||||||
|
const parallel = require('run-parallel')
|
||||||
const multiaddr = require('multiaddr')
|
const multiaddr = require('multiaddr')
|
||||||
const Peer = require('peer-info')
|
const Peer = require('peer-info')
|
||||||
const Swarm = require('../src')
|
const Swarm = require('../src')
|
||||||
const TCP = require('libp2p-tcp')
|
const TCP = require('libp2p-tcp')
|
||||||
|
const WebSockets = require('libp2p-websockets')
|
||||||
|
|
||||||
const spdy = require('libp2p-spdy')
|
const spdy = require('libp2p-spdy')
|
||||||
|
|
||||||
describe('stream muxing with spdy (on TCP)', function () {
|
describe('stream muxing with spdy (on TCP)', function () {
|
||||||
this.timeout(20000)
|
this.timeout(60 * 1000)
|
||||||
|
|
||||||
var swarmA
|
var swarmA
|
||||||
var peerA
|
var peerA
|
||||||
@ -18,11 +21,14 @@ describe('stream muxing with spdy (on TCP)', function () {
|
|||||||
var peerB
|
var peerB
|
||||||
var swarmC
|
var swarmC
|
||||||
var peerC
|
var peerC
|
||||||
|
var swarmD
|
||||||
|
var peerD
|
||||||
|
|
||||||
before((done) => {
|
before((done) => {
|
||||||
peerA = new Peer()
|
peerA = new Peer()
|
||||||
peerB = new Peer()
|
peerB = new Peer()
|
||||||
peerC = new Peer()
|
peerC = new Peer()
|
||||||
|
peerD = new Peer()
|
||||||
|
|
||||||
// console.log('peer A', peerA.id.toB58String())
|
// console.log('peer A', peerA.id.toB58String())
|
||||||
// console.log('peer B', peerB.id.toB58String())
|
// console.log('peer B', peerB.id.toB58String())
|
||||||
@ -31,48 +37,40 @@ describe('stream muxing with spdy (on TCP)', function () {
|
|||||||
peerA.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9001'))
|
peerA.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9001'))
|
||||||
peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9002'))
|
peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9002'))
|
||||||
peerC.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9003'))
|
peerC.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9003'))
|
||||||
|
peerD.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9004'))
|
||||||
|
|
||||||
swarmA = new Swarm(peerA)
|
swarmA = new Swarm(peerA)
|
||||||
swarmB = new Swarm(peerB)
|
swarmB = new Swarm(peerB)
|
||||||
swarmC = new Swarm(peerC)
|
swarmC = new Swarm(peerC)
|
||||||
|
swarmD = new Swarm(peerD)
|
||||||
|
|
||||||
swarmA.transport.add('tcp', new TCP())
|
swarmA.transport.add('tcp', new TCP())
|
||||||
swarmA.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
swarmB.transport.add('tcp', new TCP())
|
swarmB.transport.add('tcp', new TCP())
|
||||||
swarmB.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
swarmC.transport.add('tcp', new TCP())
|
swarmC.transport.add('tcp', new TCP())
|
||||||
swarmC.transport.listen('tcp', {}, null, ready)
|
swarmD.transport.add('tcp', new TCP())
|
||||||
|
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmA.transport.listen('tcp', {}, null, cb),
|
||||||
function ready () {
|
(cb) => swarmB.transport.listen('tcp', {}, null, cb),
|
||||||
if (++counter === 3) {
|
(cb) => swarmC.transport.listen('tcp', {}, null, cb),
|
||||||
done()
|
(cb) => swarmD.transport.listen('tcp', {}, null, cb)
|
||||||
}
|
], done)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
after((done) => {
|
after((done) => {
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmA.close(cb),
|
||||||
swarmA.close(closed)
|
(cb) => swarmB.close(cb),
|
||||||
swarmB.close(closed)
|
// (cb) => swarmC.close(cb)
|
||||||
swarmC.close(closed)
|
(cb) => swarmD.close(cb)
|
||||||
|
], done)
|
||||||
function closed () {
|
|
||||||
if (++counter === 3) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('add', (done) => {
|
it('add', () => {
|
||||||
swarmA.connection.addStreamMuxer(spdy)
|
swarmA.connection.addStreamMuxer(spdy)
|
||||||
swarmB.connection.addStreamMuxer(spdy)
|
swarmB.connection.addStreamMuxer(spdy)
|
||||||
swarmC.connection.addStreamMuxer(spdy)
|
swarmC.connection.addStreamMuxer(spdy)
|
||||||
done()
|
swarmD.connection.addStreamMuxer(spdy)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('handle + dial on protocol', (done) => {
|
it('handle + dial on protocol', (done) => {
|
||||||
@ -128,4 +126,119 @@ describe('stream muxing with spdy (on TCP)', function () {
|
|||||||
}, 500)
|
}, 500)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('with Identify, do getPeerInfo', (done) => {
|
||||||
|
swarmA.handle('/banana/1.0.0', (conn) => {
|
||||||
|
conn.getPeerInfo((err, peerInfoC) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(peerInfoC.id.toB58String()).to.equal(peerC.id.toB58String())
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.pipe(conn)
|
||||||
|
})
|
||||||
|
|
||||||
|
swarmC.dial(peerA, '/banana/1.0.0', (err, conn) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(Object.keys(swarmC.muxedConns).length).to.equal(1)
|
||||||
|
expect(Object.keys(swarmA.muxedConns).length).to.equal(2)
|
||||||
|
conn.getPeerInfo((err, peerInfoA) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(peerInfoA.id.toB58String()).to.equal(peerA.id.toB58String())
|
||||||
|
conn.on('end', done)
|
||||||
|
conn.resume()
|
||||||
|
conn.end()
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// This test is not possible as the raw conn is not exposed anymore
|
||||||
|
// TODO: create a similar version, but that spawns a swarm in a
|
||||||
|
// different proc
|
||||||
|
it.skip('make sure it does not blow up when the socket is closed', (done) => {
|
||||||
|
swarmD.connection.reuse()
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
const destroyed = () => ++count === 2 ? done() : null
|
||||||
|
|
||||||
|
swarmD.handle('/banana/1.0.0', (conn) => {
|
||||||
|
conn.on('error', () => {})
|
||||||
|
conn.on('close', destroyed)
|
||||||
|
})
|
||||||
|
|
||||||
|
swarmA.dial(peerD, '/banana/1.0.0', (err, conn) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
|
||||||
|
conn.on('error', () => {})
|
||||||
|
conn.on('close', destroyed)
|
||||||
|
|
||||||
|
swarmD.muxedConns[peerA.id.toB58String()].conn.destroy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// This test is not possible as the raw conn is not exposed anymore
|
||||||
|
// TODO: create a similar version, but that spawns a swarm in a
|
||||||
|
// different proc
|
||||||
|
it.skip('blow up a socket, with WebSockets', (done) => {
|
||||||
|
var swarmE
|
||||||
|
var peerE
|
||||||
|
var swarmF
|
||||||
|
var peerF
|
||||||
|
|
||||||
|
peerE = new Peer()
|
||||||
|
peerF = new Peer()
|
||||||
|
|
||||||
|
peerE.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9110/ws'))
|
||||||
|
peerF.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9120/ws'))
|
||||||
|
|
||||||
|
swarmE = new Swarm(peerE)
|
||||||
|
swarmF = new Swarm(peerF)
|
||||||
|
|
||||||
|
swarmE.transport.add('ws', new WebSockets())
|
||||||
|
swarmF.transport.add('ws', new WebSockets())
|
||||||
|
|
||||||
|
swarmE.connection.addStreamMuxer(spdy)
|
||||||
|
swarmF.connection.addStreamMuxer(spdy)
|
||||||
|
swarmE.connection.reuse()
|
||||||
|
swarmF.connection.reuse()
|
||||||
|
|
||||||
|
parallel([
|
||||||
|
(cb) => swarmE.transport.listen('ws', {}, null, cb),
|
||||||
|
(cb) => swarmF.transport.listen('ws', {}, null, cb)
|
||||||
|
], next)
|
||||||
|
|
||||||
|
function next () {
|
||||||
|
let count = 0
|
||||||
|
const destroyed = () => ++count === 2 ? close() : null
|
||||||
|
|
||||||
|
swarmE.handle('/avocado/1.0.0', (conn) => {
|
||||||
|
conn.on('error', () => {})
|
||||||
|
conn.on('close', destroyed)
|
||||||
|
})
|
||||||
|
|
||||||
|
swarmF.dial(peerE, '/avocado/1.0.0', (err, conn) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
conn.on('error', () => {})
|
||||||
|
conn.on('close', destroyed)
|
||||||
|
|
||||||
|
swarmF.muxedConns[peerE.id.toB58String()].conn.destroy()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function close () {
|
||||||
|
parallel([
|
||||||
|
(cb) => swarmE.close(cb),
|
||||||
|
(cb) => swarmF.close(cb)
|
||||||
|
], done)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('close one end, make sure the other does not blow', (done) => {
|
||||||
|
swarmC.close((err) => {
|
||||||
|
if (err) throw err
|
||||||
|
// to make sure it has time to propagate
|
||||||
|
setTimeout(done, 1000)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -4,9 +4,6 @@
|
|||||||
describe('secio conn upgrade (on TCP)', function () {
|
describe('secio conn upgrade (on TCP)', function () {
|
||||||
this.timeout(20000)
|
this.timeout(20000)
|
||||||
|
|
||||||
before((done) => { done() })
|
|
||||||
after((done) => { done() })
|
|
||||||
|
|
||||||
it.skip('add', (done) => {})
|
it.skip('add', (done) => {})
|
||||||
it.skip('dial', (done) => {})
|
it.skip('dial', (done) => {})
|
||||||
it.skip('tls on a muxed stream (not the full conn)', (done) => {})
|
it.skip('tls on a muxed stream (not the full conn)', (done) => {})
|
||||||
|
@ -2,9 +2,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
describe('tls conn upgrade (on TCP)', function () {
|
describe('tls conn upgrade (on TCP)', function () {
|
||||||
before((done) => { done() })
|
|
||||||
after((done) => { done() })
|
|
||||||
|
|
||||||
it.skip('add', (done) => {})
|
it.skip('add', (done) => {})
|
||||||
it.skip('dial', (done) => {})
|
it.skip('dial', (done) => {})
|
||||||
it.skip('tls on a muxed stream (not the full conn)', (done) => {})
|
it.skip('tls on a muxed stream (not the full conn)', (done) => {})
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
const expect = require('chai').expect
|
const expect = require('chai').expect
|
||||||
|
|
||||||
|
const parallel = require('run-parallel')
|
||||||
const multiaddr = require('multiaddr')
|
const multiaddr = require('multiaddr')
|
||||||
const Peer = require('peer-info')
|
const Peer = require('peer-info')
|
||||||
const Swarm = require('../src')
|
const Swarm = require('../src')
|
||||||
@ -21,44 +22,32 @@ describe('high level API - 1st without stream multiplexing (on TCP)', function (
|
|||||||
peerB = new Peer()
|
peerB = new Peer()
|
||||||
|
|
||||||
peerA.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9001'))
|
peerA.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9001'))
|
||||||
peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9002'))
|
peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9002/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC'))
|
||||||
|
|
||||||
swarmA = new Swarm(peerA)
|
swarmA = new Swarm(peerA)
|
||||||
swarmB = new Swarm(peerB)
|
swarmB = new Swarm(peerB)
|
||||||
|
|
||||||
swarmA.transport.add('tcp', new TCP())
|
swarmA.transport.add('tcp', new TCP())
|
||||||
swarmA.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
swarmB.transport.add('tcp', new TCP())
|
swarmB.transport.add('tcp', new TCP())
|
||||||
swarmB.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmA.transport.listen('tcp', {}, null, cb),
|
||||||
function ready () {
|
(cb) => swarmB.transport.listen('tcp', {}, null, cb)
|
||||||
if (++counter === 2) {
|
], done)
|
||||||
done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
after((done) => {
|
after((done) => {
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmA.close(cb),
|
||||||
swarmA.close(closed)
|
(cb) => swarmB.close(cb)
|
||||||
swarmB.close(closed)
|
], done)
|
||||||
|
|
||||||
function closed () {
|
|
||||||
if (++counter === 2) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('handle a protocol', (done) => {
|
it('handle a protocol', (done) => {
|
||||||
swarmB.handle('/bananas/1.0.0', (conn) => {
|
swarmB.handle('/bananas/1.0.0', (conn) => {
|
||||||
conn.pipe(conn)
|
conn.pipe(conn)
|
||||||
})
|
})
|
||||||
expect(Object.keys(swarmB.protocols).length).to.equal(1)
|
expect(Object.keys(swarmB.protocols).length).to.equal(2)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -103,4 +92,11 @@ describe('high level API - 1st without stream multiplexing (on TCP)', function (
|
|||||||
conn.on('end', done)
|
conn.on('end', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('unhandle', (done) => {
|
||||||
|
const proto = '/bananas/1.0.0'
|
||||||
|
swarmA.unhandle(proto)
|
||||||
|
expect(swarmA.protocols[proto]).to.not.exist
|
||||||
|
done()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
const expect = require('chai').expect
|
const expect = require('chai').expect
|
||||||
|
|
||||||
|
const parallel = require('run-parallel')
|
||||||
const multiaddr = require('multiaddr')
|
const multiaddr = require('multiaddr')
|
||||||
const Peer = require('peer-info')
|
const Peer = require('peer-info')
|
||||||
const Swarm = require('../src')
|
const Swarm = require('../src')
|
||||||
@ -11,7 +12,7 @@ const WebSockets = require('libp2p-websockets')
|
|||||||
const spdy = require('libp2p-spdy')
|
const spdy = require('libp2p-spdy')
|
||||||
|
|
||||||
describe('high level API - with everything mixed all together!', function () {
|
describe('high level API - with everything mixed all together!', function () {
|
||||||
this.timeout(20000)
|
this.timeout(100000)
|
||||||
|
|
||||||
var swarmA // tcp
|
var swarmA // tcp
|
||||||
var peerA
|
var peerA
|
||||||
@ -45,19 +46,13 @@ describe('high level API - with everything mixed all together!', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
after((done) => {
|
after((done) => {
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmA.close(cb),
|
||||||
swarmA.close(closed)
|
(cb) => swarmB.close(cb),
|
||||||
swarmB.close(closed)
|
// (cb) => swarmC.close(cb),
|
||||||
swarmC.close(closed)
|
(cb) => swarmD.close(cb),
|
||||||
swarmD.close(closed)
|
(cb) => swarmE.close(cb)
|
||||||
swarmE.close(closed)
|
], done)
|
||||||
|
|
||||||
function closed () {
|
|
||||||
if (++counter === 4) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('add tcp', (done) => {
|
it('add tcp', (done) => {
|
||||||
@ -66,53 +61,42 @@ describe('high level API - with everything mixed all together!', function () {
|
|||||||
peerC.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
|
peerC.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
|
||||||
|
|
||||||
swarmA.transport.add('tcp', new TCP())
|
swarmA.transport.add('tcp', new TCP())
|
||||||
swarmA.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
swarmB.transport.add('tcp', new TCP())
|
swarmB.transport.add('tcp', new TCP())
|
||||||
swarmB.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
swarmC.transport.add('tcp', new TCP())
|
swarmC.transport.add('tcp', new TCP())
|
||||||
swarmC.transport.listen('tcp', {}, null, ready)
|
|
||||||
|
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmA.transport.listen('tcp', {}, null, cb),
|
||||||
function ready () {
|
(cb) => swarmB.transport.listen('tcp', {}, null, cb)
|
||||||
if (++counter === 3) {
|
// (cb) => swarmC.transport.listen('tcp', {}, null, cb)
|
||||||
done()
|
], done)
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('add utp', (done) => {})
|
it.skip('add utp', (done) => {})
|
||||||
|
|
||||||
it('add websockets', (done) => {
|
it('add websockets', (done) => {
|
||||||
peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9012/websockets'))
|
peerB.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9012/ws'))
|
||||||
peerC.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9022/websockets'))
|
peerC.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9022/ws'))
|
||||||
peerD.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9032/websockets'))
|
peerD.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9032/ws'))
|
||||||
peerE.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9042/websockets'))
|
peerE.multiaddr.add(multiaddr('/ip4/127.0.0.1/tcp/9042/ws'))
|
||||||
|
|
||||||
swarmB.transport.add('ws', new WebSockets())
|
swarmB.transport.add('ws', new WebSockets())
|
||||||
swarmB.transport.listen('ws', {}, null, ready)
|
|
||||||
|
|
||||||
swarmC.transport.add('ws', new WebSockets())
|
swarmC.transport.add('ws', new WebSockets())
|
||||||
swarmC.transport.listen('ws', {}, null, ready)
|
|
||||||
|
|
||||||
swarmD.transport.add('ws', new WebSockets())
|
swarmD.transport.add('ws', new WebSockets())
|
||||||
swarmD.transport.listen('ws', {}, null, ready)
|
|
||||||
|
|
||||||
swarmE.transport.add('ws', new WebSockets())
|
swarmE.transport.add('ws', new WebSockets())
|
||||||
swarmE.transport.listen('ws', {}, null, ready)
|
|
||||||
|
|
||||||
var counter = 0
|
parallel([
|
||||||
|
(cb) => swarmB.transport.listen('ws', {}, null, cb),
|
||||||
function ready () {
|
// (cb) => swarmC.transport.listen('ws', {}, null, cb),
|
||||||
if (++counter === 4) {
|
(cb) => swarmD.transport.listen('ws', {}, null, cb),
|
||||||
done()
|
(cb) => swarmE.transport.listen('ws', {}, null, cb)
|
||||||
}
|
], done)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('add spdy', (done) => {
|
it('listen automatically', (done) => {
|
||||||
|
swarmC.listen(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add spdy', () => {
|
||||||
swarmA.connection.addStreamMuxer(spdy)
|
swarmA.connection.addStreamMuxer(spdy)
|
||||||
swarmB.connection.addStreamMuxer(spdy)
|
swarmB.connection.addStreamMuxer(spdy)
|
||||||
swarmC.connection.addStreamMuxer(spdy)
|
swarmC.connection.addStreamMuxer(spdy)
|
||||||
@ -124,13 +108,37 @@ describe('high level API - with everything mixed all together!', function () {
|
|||||||
swarmC.connection.reuse()
|
swarmC.connection.reuse()
|
||||||
swarmD.connection.reuse()
|
swarmD.connection.reuse()
|
||||||
swarmE.connection.reuse()
|
swarmE.connection.reuse()
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('add multiplex', (done) => {})
|
it.skip('add multiplex', () => {})
|
||||||
|
|
||||||
it('dial from tcp to tcp+ws', (done) => {
|
it('warm up from A to B on tcp to tcp+ws', (done) => {
|
||||||
|
parallel([
|
||||||
|
(cb) => swarmB.once('peer-mux-established', (peerInfo) => {
|
||||||
|
expect(peerInfo.id.toB58String()).to.equal(peerA.id.toB58String())
|
||||||
|
cb()
|
||||||
|
}),
|
||||||
|
(cb) => swarmA.once('peer-mux-established', (peerInfo) => {
|
||||||
|
expect(peerInfo.id.toB58String()).to.equal(peerB.id.toB58String())
|
||||||
|
cb()
|
||||||
|
}),
|
||||||
|
(cb) => swarmA.dial(peerB, (err) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(Object.keys(swarmA.muxedConns).length).to.equal(1)
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
], done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('warm up a warmed up, from B to A', (done) => {
|
||||||
|
swarmB.dial(peerA, (err) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(Object.keys(swarmA.muxedConns).length).to.equal(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dial from tcp to tcp+ws, on protocol', (done) => {
|
||||||
swarmB.handle('/anona/1.0.0', (conn) => {
|
swarmB.handle('/anona/1.0.0', (conn) => {
|
||||||
conn.pipe(conn)
|
conn.pipe(conn)
|
||||||
})
|
})
|
||||||
@ -145,6 +153,14 @@ describe('high level API - with everything mixed all together!', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('dial from ws to ws no proto', (done) => {
|
||||||
|
swarmD.dial(peerE, (err) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(Object.keys(swarmD.muxedConns).length).to.equal(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('dial from ws to ws', (done) => {
|
it('dial from ws to ws', (done) => {
|
||||||
swarmE.handle('/abacaxi/1.0.0', (conn) => {
|
swarmE.handle('/abacaxi/1.0.0', (conn) => {
|
||||||
conn.pipe(conn)
|
conn.pipe(conn)
|
||||||
@ -182,11 +198,19 @@ describe('high level API - with everything mixed all together!', function () {
|
|||||||
|
|
||||||
it('dial from tcp+ws to tcp+ws', (done) => {
|
it('dial from tcp+ws to tcp+ws', (done) => {
|
||||||
swarmC.handle('/mamao/1.0.0', (conn) => {
|
swarmC.handle('/mamao/1.0.0', (conn) => {
|
||||||
|
conn.getPeerInfo((err, peerInfo) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(peerInfo).to.exist
|
||||||
|
})
|
||||||
conn.pipe(conn)
|
conn.pipe(conn)
|
||||||
})
|
})
|
||||||
|
|
||||||
swarmA.dial(peerC, '/mamao/1.0.0', (err, conn) => {
|
swarmA.dial(peerC, '/mamao/1.0.0', (err, conn) => {
|
||||||
expect(err).to.not.exist
|
expect(err).to.not.exist
|
||||||
|
conn.getPeerInfo((err, peerInfo) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(peerInfo).to.exist
|
||||||
|
})
|
||||||
expect(Object.keys(swarmA.muxedConns).length).to.equal(2)
|
expect(Object.keys(swarmA.muxedConns).length).to.equal(2)
|
||||||
conn.end()
|
conn.end()
|
||||||
|
|
||||||
@ -194,4 +218,31 @@ describe('high level API - with everything mixed all together!', function () {
|
|||||||
conn.on('end', done)
|
conn.on('end', done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('hangUp', (done) => {
|
||||||
|
let count = 0
|
||||||
|
const ready = () => ++count === 3 ? done() : null
|
||||||
|
|
||||||
|
swarmB.once('peer-mux-closed', (peerInfo) => {
|
||||||
|
expect(Object.keys(swarmB.muxedConns).length).to.equal(0)
|
||||||
|
ready()
|
||||||
|
})
|
||||||
|
|
||||||
|
swarmA.once('peer-mux-closed', (peerInfo) => {
|
||||||
|
expect(Object.keys(swarmA.muxedConns).length).to.equal(1)
|
||||||
|
ready()
|
||||||
|
})
|
||||||
|
|
||||||
|
swarmA.hangUp(peerB, (err) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
ready()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('close a muxer emits event', (done) => {
|
||||||
|
parallel([
|
||||||
|
(cb) => swarmC.close(cb),
|
||||||
|
(cb) => swarmA.once('peer-mux-closed', (peerInfo) => cb())
|
||||||
|
], done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
48
test/browser-00-transport-websockets.js
Normal file
48
test/browser-00-transport-websockets.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/* eslint-env mocha */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const expect = require('chai').expect
|
||||||
|
const multiaddr = require('multiaddr')
|
||||||
|
const Id = require('peer-id')
|
||||||
|
const Peer = require('peer-info')
|
||||||
|
const WebSockets = require('libp2p-websockets')
|
||||||
|
const bl = require('bl')
|
||||||
|
|
||||||
|
const Swarm = require('../src')
|
||||||
|
|
||||||
|
describe('transport - websockets', function () {
|
||||||
|
this.timeout(10000)
|
||||||
|
|
||||||
|
var swarm
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
const b58IdSrc = 'QmYzgdesgjdvD3okTPGZT9NPmh1BuH5FfTVNKjsvaAprhb'
|
||||||
|
// use a pre generated Id to save time
|
||||||
|
const idSrc = Id.createFromB58String(b58IdSrc)
|
||||||
|
const peerSrc = new Peer(idSrc)
|
||||||
|
swarm = new Swarm(peerSrc)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add', (done) => {
|
||||||
|
swarm.transport.add('ws', new WebSockets(), () => {
|
||||||
|
expect(Object.keys(swarm.transports).length).to.equal(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dial', (done) => {
|
||||||
|
const ma = multiaddr('/ip4/127.0.0.1/tcp/9100/ws')
|
||||||
|
|
||||||
|
const conn = swarm.transport.dial('ws', ma, (err, conn) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.pipe(bl((err, data) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(data.toString()).to.equal('hey')
|
||||||
|
done()
|
||||||
|
}))
|
||||||
|
conn.write('hey')
|
||||||
|
conn.end()
|
||||||
|
})
|
||||||
|
})
|
90
test/browser-01-transport-webrtc-star.js
Normal file
90
test/browser-01-transport-webrtc-star.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/* eslint-env mocha */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const expect = require('chai').expect
|
||||||
|
const multiaddr = require('multiaddr')
|
||||||
|
const peerId = require('peer-id')
|
||||||
|
const PeerInfo = require('peer-info')
|
||||||
|
const WebRTCStar = require('libp2p-webrtc-star')
|
||||||
|
const bl = require('bl')
|
||||||
|
const parallel = require('run-parallel')
|
||||||
|
|
||||||
|
const Swarm = require('../src')
|
||||||
|
|
||||||
|
describe('transport - webrtc-star', function () {
|
||||||
|
this.timeout(10000)
|
||||||
|
|
||||||
|
let swarm1
|
||||||
|
let peer1
|
||||||
|
|
||||||
|
let swarm2
|
||||||
|
let peer2
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
const id1 = peerId.createFromB58String('QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooooA')
|
||||||
|
peer1 = new PeerInfo(id1)
|
||||||
|
const mh1 = multiaddr('/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooooA')
|
||||||
|
peer1.multiaddr.add(mh1)
|
||||||
|
|
||||||
|
const id2 = peerId.createFromB58String('QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooooB')
|
||||||
|
peer2 = new PeerInfo(id2)
|
||||||
|
const mh2 = multiaddr('/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooooB')
|
||||||
|
peer2.multiaddr.add(mh2)
|
||||||
|
|
||||||
|
swarm1 = new Swarm(peer1)
|
||||||
|
swarm2 = new Swarm(peer2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add WebRTCStar transport to swarm 1', (done) => {
|
||||||
|
swarm1.transport.add('wstar', new WebRTCStar(), () => {
|
||||||
|
expect(Object.keys(swarm1.transports).length).to.equal(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add WebRTCStar transport to swarm 2', (done) => {
|
||||||
|
swarm2.transport.add('wstar', new WebRTCStar(), () => {
|
||||||
|
expect(Object.keys(swarm2.transports).length).to.equal(1)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('listen on swarm 1', (done) => {
|
||||||
|
swarm1.transport.listen('wstar', {}, (conn) => {
|
||||||
|
conn.pipe(conn)
|
||||||
|
}, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('listen on swarm 2', (done) => {
|
||||||
|
swarm2.transport.listen('wstar', {}, (conn) => {
|
||||||
|
conn.pipe(conn)
|
||||||
|
}, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dial', (done) => {
|
||||||
|
swarm1.transport.dial('wstar', peer2.multiaddrs[0], (err, conn) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
|
||||||
|
const text = 'Hello World'
|
||||||
|
conn.pipe(bl((err, data) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(data.toString()).to.equal(text)
|
||||||
|
done()
|
||||||
|
}))
|
||||||
|
|
||||||
|
conn.write(text)
|
||||||
|
conn.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('close', (done) => {
|
||||||
|
parallel([
|
||||||
|
(cb) => {
|
||||||
|
swarm1.transport.close('wstar', cb)
|
||||||
|
},
|
||||||
|
(cb) => {
|
||||||
|
swarm2.transport.close('wstar', cb)
|
||||||
|
}
|
||||||
|
], done)
|
||||||
|
})
|
||||||
|
})
|
74
test/browser-02-swarm-with-muxing-plus-websockets.js
Normal file
74
test/browser-02-swarm-with-muxing-plus-websockets.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/* eslint-env mocha */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const expect = require('chai').expect
|
||||||
|
const multiaddr = require('multiaddr')
|
||||||
|
const PeerId = require('peer-id')
|
||||||
|
const PeerInfo = require('peer-info')
|
||||||
|
const WebSockets = require('libp2p-websockets')
|
||||||
|
const spdy = require('libp2p-spdy')
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const Swarm = require('../src')
|
||||||
|
|
||||||
|
describe('high level API (swarm with spdy + websockets)', function () {
|
||||||
|
this.timeout(60 * 1000)
|
||||||
|
|
||||||
|
var swarm
|
||||||
|
var peerDst
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
const peerSrc = new PeerInfo()
|
||||||
|
swarm = new Swarm(peerSrc)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add spdy', () => {
|
||||||
|
swarm.connection.addStreamMuxer(spdy)
|
||||||
|
swarm.connection.reuse()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add ws', () => {
|
||||||
|
swarm.transport.add('ws', new WebSockets())
|
||||||
|
expect(Object.keys(swarm.transports).length).to.equal(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('create Dst peer info', () => {
|
||||||
|
const id = PeerId.createFromJSON(
|
||||||
|
JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
path.join(__dirname, './test-data/id-2.json'))))
|
||||||
|
|
||||||
|
peerDst = new PeerInfo(id)
|
||||||
|
|
||||||
|
const ma = multiaddr('/ip4/127.0.0.1/tcp/9200/ws')
|
||||||
|
peerDst.multiaddr.add(ma)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dial to warm a conn', (done) => {
|
||||||
|
swarm.dial(peerDst, (err) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dial on protocol, use warmed conn', (done) => {
|
||||||
|
swarm.dial(peerDst, '/echo/1.0.0', (err, conn) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
conn.end()
|
||||||
|
conn.on('data', () => {}) // let it flow.. let it flooooow
|
||||||
|
conn.on('end', done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('close', (done) => {
|
||||||
|
// cause CI is slow
|
||||||
|
setTimeout(() => {
|
||||||
|
swarm.close(done)
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO - test that the listener (node.js peer) can dial back
|
||||||
|
// do that by dialing on a protocol to activate that behaviour
|
||||||
|
// like libp2p-spdy tests
|
||||||
|
})
|
129
test/browser-03-swarm-with-muxing-plus-webrtc-star.js
Normal file
129
test/browser-03-swarm-with-muxing-plus-webrtc-star.js
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/* eslint-env mocha */
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const expect = require('chai').expect
|
||||||
|
const multiaddr = require('multiaddr')
|
||||||
|
const peerId = require('peer-id')
|
||||||
|
const PeerInfo = require('peer-info')
|
||||||
|
const WebRTCStar = require('libp2p-webrtc-star')
|
||||||
|
const spdy = require('libp2p-spdy')
|
||||||
|
const bl = require('bl')
|
||||||
|
const parallel = require('run-parallel')
|
||||||
|
|
||||||
|
const Swarm = require('../src')
|
||||||
|
|
||||||
|
describe('high level API (swarm with spdy + webrtc-star)', function () {
|
||||||
|
this.timeout(60 * 1000)
|
||||||
|
|
||||||
|
let swarm1
|
||||||
|
let peer1
|
||||||
|
let wstar1
|
||||||
|
|
||||||
|
let swarm2
|
||||||
|
let peer2
|
||||||
|
let wstar2
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
const id1 = peerId.create()
|
||||||
|
peer1 = new PeerInfo(id1)
|
||||||
|
const mh1 = multiaddr('/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + id1.toB58String())
|
||||||
|
peer1.multiaddr.add(mh1)
|
||||||
|
|
||||||
|
const id2 = peerId.create()
|
||||||
|
peer2 = new PeerInfo(id2)
|
||||||
|
const mh2 = multiaddr('/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + id2.toB58String())
|
||||||
|
peer2.multiaddr.add(mh2)
|
||||||
|
|
||||||
|
swarm1 = new Swarm(peer1)
|
||||||
|
swarm2 = new Swarm(peer2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add WebRTCStar transport to swarm 1', () => {
|
||||||
|
wstar1 = new WebRTCStar()
|
||||||
|
swarm1.transport.add('wstar', wstar1)
|
||||||
|
expect(Object.keys(swarm1.transports).length).to.equal(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add WebRTCStar transport to swarm 2', () => {
|
||||||
|
wstar2 = new WebRTCStar()
|
||||||
|
swarm2.transport.add('wstar', wstar2)
|
||||||
|
expect(Object.keys(swarm2.transports).length).to.equal(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('listen on swarm 1', (done) => {
|
||||||
|
swarm1.listen(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('listen on swarm 2', (done) => {
|
||||||
|
swarm2.listen(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add spdy', () => {
|
||||||
|
swarm1.connection.addStreamMuxer(spdy)
|
||||||
|
swarm1.connection.reuse()
|
||||||
|
swarm2.connection.addStreamMuxer(spdy)
|
||||||
|
swarm2.connection.reuse()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handle proto', () => {
|
||||||
|
swarm2.handle('/echo/1.0.0', (conn) => {
|
||||||
|
conn.pipe(conn)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dial on proto', (done) => {
|
||||||
|
swarm1.dial(peer2, '/echo/1.0.0', (err, conn) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(Object.keys(swarm1.muxedConns).length).to.equal(1)
|
||||||
|
|
||||||
|
const text = 'Hello World'
|
||||||
|
conn.pipe(bl((err, data) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(data.toString()).to.equal(text)
|
||||||
|
expect(Object.keys(swarm2.muxedConns).length).to.equal(1)
|
||||||
|
done()
|
||||||
|
}))
|
||||||
|
|
||||||
|
conn.write(text)
|
||||||
|
conn.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('create a third node and check that discovery works', (done) => {
|
||||||
|
wstar1.discovery.on('peer', (peerInfo) => {
|
||||||
|
expect(Object.keys(swarm1.muxedConns).length).to.equal(1)
|
||||||
|
swarm1.dial(peerInfo, () => {
|
||||||
|
expect(Object.keys(swarm1.muxedConns).length).to.equal(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
wstar2.discovery.on('peer', (peerInfo) => {
|
||||||
|
swarm2.dial(peerInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
const id3 = peerId.create()
|
||||||
|
const peer3 = new PeerInfo(id3)
|
||||||
|
const mh3 = multiaddr('/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + id3.toB58String())
|
||||||
|
peer3.multiaddr.add(mh3)
|
||||||
|
|
||||||
|
const swarm3 = new Swarm(peer3)
|
||||||
|
const wstar3 = new WebRTCStar()
|
||||||
|
swarm3.transport.add('wstar', wstar3)
|
||||||
|
swarm3.connection.addStreamMuxer(spdy)
|
||||||
|
swarm3.connection.reuse()
|
||||||
|
swarm3.listen(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(Object.keys(swarm1.muxedConns).length).to.equal(2)
|
||||||
|
expect(Object.keys(swarm2.muxedConns).length).to.equal(2)
|
||||||
|
expect(Object.keys(swarm3.muxedConns).length).to.equal(2)
|
||||||
|
swarm3.close(done)
|
||||||
|
}, 8000)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('close', (done) => {
|
||||||
|
parallel([
|
||||||
|
swarm1.close,
|
||||||
|
swarm2.close
|
||||||
|
], done)
|
||||||
|
})
|
||||||
|
})
|
118
test/browser.js
118
test/browser.js
@ -2,11 +2,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const expect = require('chai').expect
|
const expect = require('chai').expect
|
||||||
const multiaddr = require('multiaddr')
|
const w = require('webrtcsupport')
|
||||||
const Id = require('peer-id')
|
|
||||||
const Peer = require('peer-info')
|
|
||||||
const WebSockets = require('libp2p-websockets')
|
|
||||||
const bl = require('bl')
|
|
||||||
|
|
||||||
const Swarm = require('../src')
|
const Swarm = require('../src')
|
||||||
|
|
||||||
@ -17,109 +13,15 @@ describe('basics', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('transport - websockets', function () {
|
require('./browser-00-transport-websockets.js')
|
||||||
this.timeout(10000)
|
|
||||||
|
|
||||||
var swarm
|
if (w.support) {
|
||||||
|
require('./browser-01-transport-webrtc-star.js')
|
||||||
|
}
|
||||||
|
|
||||||
before((done) => {
|
require('./browser-02-swarm-with-muxing-plus-websockets.js')
|
||||||
const b58IdSrc = 'QmYzgdesgjdvD3okTPGZT9NPmh1BuH5FfTVNKjsvaAprhb'
|
|
||||||
// use a pre generated Id to save time
|
|
||||||
const idSrc = Id.createFromB58String(b58IdSrc)
|
|
||||||
const peerSrc = new Peer(idSrc)
|
|
||||||
swarm = new Swarm(peerSrc)
|
|
||||||
|
|
||||||
done()
|
if (w.support) {
|
||||||
})
|
require('./browser-03-swarm-with-muxing-plus-webrtc-star.js')
|
||||||
|
require('./browser-04-swarm-with-muxing-plus-websockets-and-webrtc-star.js')
|
||||||
it('add', (done) => {
|
}
|
||||||
swarm.transport.add('ws', new WebSockets(), () => {
|
|
||||||
expect(Object.keys(swarm.transports).length).to.equal(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('dial', (done) => {
|
|
||||||
const ma = multiaddr('/ip4/127.0.0.1/tcp/9100/websockets')
|
|
||||||
|
|
||||||
const conn = swarm.transport.dial('ws', ma, (err, conn) => {
|
|
||||||
expect(err).to.not.exist
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.pipe(bl((err, data) => {
|
|
||||||
expect(err).to.not.exist
|
|
||||||
expect(data.toString()).to.equal('hey')
|
|
||||||
done()
|
|
||||||
}))
|
|
||||||
conn.write('hey')
|
|
||||||
conn.end()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('high level API - 1st without stream multiplexing (on websockets)', function () {
|
|
||||||
this.timeout(10000)
|
|
||||||
|
|
||||||
var swarm
|
|
||||||
var peerDst
|
|
||||||
|
|
||||||
before((done) => {
|
|
||||||
const b58IdSrc = 'QmYzgdesgjdvD3okTPGZT9NPmh1BuH5FfTVNKjsvaAprhb'
|
|
||||||
// use a pre generated Id to save time
|
|
||||||
const idSrc = Id.createFromB58String(b58IdSrc)
|
|
||||||
const peerSrc = new Peer(idSrc)
|
|
||||||
swarm = new Swarm(peerSrc)
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
after((done) => {
|
|
||||||
done()
|
|
||||||
// swarm.close(done)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('add ws', (done) => {
|
|
||||||
swarm.transport.add('ws', new WebSockets())
|
|
||||||
expect(Object.keys(swarm.transports).length).to.equal(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('create Dst peer info', (done) => {
|
|
||||||
const b58IdDst = 'QmYzgdesgjdvD3okTPGZT9NPmh1BuH5FfTVNKjsvaAprhb'
|
|
||||||
// use a pre generated Id to save time
|
|
||||||
const idDst = Id.createFromB58String(b58IdDst)
|
|
||||||
peerDst = new Peer(idDst)
|
|
||||||
|
|
||||||
const ma = multiaddr('/ip4/127.0.0.1/tcp/9200/websockets')
|
|
||||||
peerDst.multiaddr.add(ma)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('dial on protocol', (done) => {
|
|
||||||
swarm.dial(peerDst, '/echo/1.0.0', (err, conn) => {
|
|
||||||
expect(err).to.not.exist
|
|
||||||
conn.pipe(bl((err, data) => {
|
|
||||||
expect(err).to.not.exist
|
|
||||||
expect(data.toString()).to.equal('hey')
|
|
||||||
done()
|
|
||||||
}))
|
|
||||||
conn.write('hey')
|
|
||||||
conn.end()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('dial to warm a conn', (done) => {
|
|
||||||
swarm.dial(peerDst, (err) => {
|
|
||||||
expect(err).to.not.exist
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('dial on protocol, reuse warmed conn', (done) => {
|
|
||||||
swarm.dial(peerDst, '/echo/1.0.0', (err, conn) => {
|
|
||||||
expect(err).to.not.exist
|
|
||||||
conn.end()
|
|
||||||
conn.on('data', () => {}) // let it flow.. let it flooooow
|
|
||||||
conn.on('end', done)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
5
test/test-data/id-1.json
Normal file
5
test/test-data/id-1.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "QmYmfUS4A3E64BzU8DsCmCWpPhcXWU2KTKNRGtdtN4oCgU",
|
||||||
|
"privKey": "CAASqAkwggSkAgEAAoIBAQCYtGLh+ow9WEJMn50voPGa6MsqSgJx8pNXGtk5kMSktWxfYHrejLZJjN0+br2CwpFMtf9JW6dAIpxb3qViBCFXjzEK8JuYaXM2sHC6sapyCxeZUbZJtGAXNWQW3qV7m8s8cJTOu2s1euT/G6uf/mIVFIzCkQDx+Ejh5Aie+BTAEf1WbLmcoDDxVESe22gpTxtMG8WTocMV34BxKn8d8vhcZZsi8LLkjg172QwQr3Q68jKgdja3K1YYm6fnso6H3+H06IHgPFAvVhycBbmlyR3bL/hFBl6+ElwBxeIrlM/oAY93KCs622SLYWFHb+J2q7WofSbUSscp3gWj7c8KJqHvAgMBAAECggEBAJZi4BcpBj/L0c9gSg8D86zZomvNY0cQ3GYmPNPibKbBPS9Y9uiBr2wT3DeGHADQ2QOxIO7/4mDZNR+Mz1cONj/i9yuM9c9N2nd7oClcmz2hCualgF5p01BH9oBHWLW5IpgtT3+hN939X9SVTZpNjg6wpEdhQosKN8yvJIZaTyUvh/ZMRIJvbnbLg13gIF7Lpyn1rtFovQg0dET0C8zhTCDPacJIOLp8BIBMknPfOl0SrvOMZjufzVZLvbt0YraXhLK8EWe87ffTMoBlIktWpEKdPBOCuFf4E4WRXJ78tcbvNtx3f5zGi+ZVbKcLA1axu+OqbjHCG6yrlywcVBoTuxECgYEA56yDBaM0VFD1CqsqwYIWmAyYBjV7dkM+ogMb+mfQn+ja6QSt+U/APXB3dP+EDvysh5AZR0wpUrmz14xC1yB1/XAKIfMLQZB8DdUkuj5UcsKjkzLJkIFYGOXIutU7IHTma7s/0fLxwp8SvkEL+6nHuZskf77yjDAvWLZeSD/CYWsCgYEAqL0mKeyyhBBFvNJyE3CyyhDfzgf+NrvrNJcx73nAzLDE44BPc/3lHYn2AJJhasNnjJfRiFzW90PNgCjZLLXqeHkX4xixoibvRtb31WHR2UyxXe/KQZwBy11mPzStnI4Y83C2A8OXsx4xAPq69nX9foSFD6cuLkWUGeb8f7Jxbo0CgYB25mfcJdW+jEom7pAj/kLgSF5hmWNC3+IuPhBG5K8C0vw+6ULsmEyee7EjX9wD4RQfAwqmN+VhaqNtNbQ8OpGzv6PDprwZKzEv3DtcRo8K0vAmpMMkIe334T6y/Kq6zqRPmCt58gi4DPIOqM2gnJM/o+sIkRRkdHpoOjiLNgXp/wKBgQCNrGpLjwl/am4zEHppKhljIPHX+cwORo8/06ZAi/g9pDlbThLnr4fb2kaqyjxyuGfLmnh5xoFSkCINdb6KFJ8t0XYl3UjffVMvJjRle0EG8qaE2Vz24zZ6egvsC52ssX3vf3XDCUjoQfQg/2NUpVJWFIvnzZUvkom7ib38tWUZzQKBgDe0+OqdJEIdajkwCMEYbmZDYqkbw4pgmwSqCwK7HeCi8dvACW5OCCutnN0L57eEltyWy0XP2XmRlfsD0atkKBq3KgNfSawx6/t/K3OtZa8VAtg2M0PbCZljW/8Bz6xlxiyPXFTRgr9zr4yM1homMmPA39hURmXNNedXUh3IMkH7",
|
||||||
|
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYtGLh+ow9WEJMn50voPGa6MsqSgJx8pNXGtk5kMSktWxfYHrejLZJjN0+br2CwpFMtf9JW6dAIpxb3qViBCFXjzEK8JuYaXM2sHC6sapyCxeZUbZJtGAXNWQW3qV7m8s8cJTOu2s1euT/G6uf/mIVFIzCkQDx+Ejh5Aie+BTAEf1WbLmcoDDxVESe22gpTxtMG8WTocMV34BxKn8d8vhcZZsi8LLkjg172QwQr3Q68jKgdja3K1YYm6fnso6H3+H06IHgPFAvVhycBbmlyR3bL/hFBl6+ElwBxeIrlM/oAY93KCs622SLYWFHb+J2q7WofSbUSscp3gWj7c8KJqHvAgMBAAE="
|
||||||
|
}
|
5
test/test-data/id-2.json
Normal file
5
test/test-data/id-2.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": "QmQAbW9j3wQ8JDFmg8JRid82EpZabuCngVDmhqzCmJwqt6",
|
||||||
|
"privKey": "CAASpgkwggSiAgEAAoIBAQCAQjiCzMF+PQaDUuNa7avUsj2xnNTQcUrs4yHz/L+JI/AY2ij0iXsBSE0chK1KtBu24gZzWs3/BDyNl28E0Sd41QpK6oTVMHjUfLovO+h7G78bqpI83vk5CEOKt29VihQs282fivbQb5ALYwzBIW2lsIoWwrQq1btsNA5NXJ43OAcPZ9SybBUg49f5gWf/kmh/J6e1rvwyVjQc7cmmpzcQUc+XNL7db6T3ArokXZMyBK6oQCOaJc1bqwgHwYSI3parjds9k8Z6fXA2ub3Va//1EgjQ50lRZH03PGYS42HR1QSSz1eLjMmdrbJrZZj7IbXgqAO6gT6wlGLr5xMQudabAgMBAAECggEAQ9NBESJ4fGqHJDFUG8St5pevelqGTAhtZ+IhFWamXz6K/Il5uP9u9dmnNZqQDX47XbYfVSdC4kX6Q6I+SlzUs9htTfrA7gBpFW00BEB5C4k7wcSs+tWrE9bj6NpiXOjdDG/cSC9zn/wvP2ZM22DzG/jEvY6POku2hlzs50pAPNB7bBaKysA/e52J0Tu/Wf/+sZyp2MiYQJmIkfbYeDF2rqm5y04S6Z31O3SMQIETNcBK8T+L2jwx+Q0msB8toam7hRf1KjxD0yZe+Vff9tPfwjgEoWF+O27g3+rjDq/QqUfzOPMgvAFgELBMpv6CCM8/3l9gUu+7itBxDq65sDCoCQKBgQC6FTLTQA3ux3WV0/7MKXJIHgYZ4b8lIbiiWuO/6t2ZnwvLfTbiU5br/8bcRPL5ygFuIdzkx8VHcbkOmld/VE7qaRZoJb94JVvC6N+5MQxr+pzbWQSNcE+cKJgy1RADea8nad698ifls/39kZGCc6Srt2TqxTBuoZ3c9jEMs3N2pwKBgQCwcxNSw7Wkq302lKc/7QdtfegrwlLjRClLYaW9ESQeErayRY8pxLgl/XKap1HPyc0aQ+78W6w+DAxvcToGBsLak0ujJjzP7b8G6fo+cexuIr8NiGL4LVzpZfQjkfQU4DDwsOdedeKzGelIdstMMtAZDFG9eNPe99XeJBnYfIDS7QKBgH8xFjiHQ/6+n4T2DueGPPNGcm0mfPzoe8ed0KbR5v6mU+2XfPheon5VqpvNFTff9/JLey11z0byWMe+f6gs/HQFuKcfhiydfIdRnfp7qD32Y1kbE52J8yCOLtowAG4fsrWCDBpRdyvvR+EWqxs76IbnKDfA6UX1em4aaZSA5J9pAoGAE8aB5ue6Rt9VZDWa3QZCq9nNmIHp6kCsZB9ohN0T8C7mvOog1myOuutB2eVgvOoAC66LbUsU7ctJ5X+KIjzFv9t8Qae6bw9VNoAopLD974YDZY/gj7H91Maxav8jnOdXdNJOy/5oTuxbgdyWgk67leMUkiiljjq2hHQFVYb2pS0CgYBam0ZJ5Trds1LijE2eoYPyiJdhWEsHYFDzoV17cyjhbSrmlWJBNKQfw6q6UtnxSNFMvsPOZv53d3B8iIDnZ/UHFvw1et+yQk/QrxTfXurqn8lJcMCfKzm3ORKibgJPMmtcPbLoxuEKXMXx18iwoCsMnapijJ0Qj5HofluiupSfxg==",
|
||||||
|
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCAQjiCzMF+PQaDUuNa7avUsj2xnNTQcUrs4yHz/L+JI/AY2ij0iXsBSE0chK1KtBu24gZzWs3/BDyNl28E0Sd41QpK6oTVMHjUfLovO+h7G78bqpI83vk5CEOKt29VihQs282fivbQb5ALYwzBIW2lsIoWwrQq1btsNA5NXJ43OAcPZ9SybBUg49f5gWf/kmh/J6e1rvwyVjQc7cmmpzcQUc+XNL7db6T3ArokXZMyBK6oQCOaJc1bqwgHwYSI3parjds9k8Z6fXA2ub3Va//1EgjQ50lRZH03PGYS42HR1QSSz1eLjMmdrbJrZZj7IbXgqAO6gT6wlGLr5xMQudabAgMBAAE="
|
||||||
|
}
|
29552
vendor/forge.bundle.js
vendored
Normal file
29552
vendor/forge.bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user