Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hyperswarm

Package Overview
Dependencies
Maintainers
4
Versions
90
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hyperswarm - npm Package Compare versions

Comparing version 2.3.1 to 2.4.0

test/validate-peer.js

10

lib/peer-info.js
'use strict'
const { EventEmitter } = require('events')
module.exports = peer => new PeerInfo(peer)
module.exports = (peer, queue) => new PeerInfo(peer, queue)

@@ -17,3 +17,3 @@ const PROVEN = 0b1

class PeerInfo extends EventEmitter {
constructor (peer = null) {
constructor (peer = null, queue) {
super()

@@ -26,4 +26,6 @@ this.priority = (peer && peer.local) ? 3 : 2

this.stream = null
this.duplicate = null
this.topics = []
this._index = 0
this._queue = queue
}

@@ -43,2 +45,6 @@

deduplicate (remoteId, localId) {
return this._queue.deduplicate(remoteId, localId, this)
}
backoff () {

@@ -45,0 +51,0 @@ // server can not backoff:

54

lib/queue.js

@@ -53,6 +53,56 @@ 'use strict'

})
this._dedup = new Map()
}
deduplicate (localId, remoteId, peer) {
const id = localId.toString('hex') + '\n' + remoteId.toString('hex')
const other = this._dedup.get(id)
if (!other) {
this._dedup.set(id, peer)
peer.stream.on('close', this._ondedupclose.bind(this, peer, id))
return false
}
const cmp = Buffer.compare(localId, remoteId)
if (cmp === 0) {
// destroy and ban connections to ourself
other.destroy(new Error('Connected to self'))
peer.destroy(new Error('Connected to self'))
return true
}
if (cmp < 0 ? peer.client : !peer.client) {
if (other.client && !other.banned) peer.duplicate = other
other.destroy(new Error('Duplicate connection'))
this._dedup.set(id, peer)
peer.stream.on('close', this._ondedupclose.bind(this, peer, id))
return false
}
if (peer.client && !peer.banned) other.duplicate = peer
peer.destroy(new Error('Duplicate connection'))
return true
}
_ondedupclose (peer, id) {
if (this._dedup.get(id) === peer) this._dedup.delete(id)
if (!peer.duplicate || !peer.duplicate.client || !peer.duplicate.banned) return
// double check that this is exactly the one we banned earlier
if (this._infos === null || this._infos.get(toID(peer.duplicate.peer, this._multiplex)) !== peer.duplicate) return
// remove and add to give it lowest possible prio
this.remove(peer.duplicate.peer)
this.add(peer.duplicate.peer)
}
_release (batch) {
for (const info of batch) this.remove(info.peer)
}
_push (batch) {

@@ -105,3 +155,3 @@ const empty = !this._queue.head()

if (!info) {
info = peerInfo(peer)
info = peerInfo(peer, this)
this._infos.set(id, info)

@@ -123,3 +173,3 @@ }

const id = toID(peer)
const id = toID(peer, this._multiplex)
const info = this._infos.get(id)

@@ -126,0 +176,0 @@

{
"name": "hyperswarm",
"version": "2.3.1",
"version": "2.4.0",
"description": "A distributed networking stack for connecting peers",

@@ -5,0 +5,0 @@ "main": "swarm.js",

@@ -61,3 +61,5 @@ # hyperswarm

maxClientSockets: Infinity,
// configure peer management behaviour
// apply a filter before connecting to the peer
validatePeer: (peer) => true,
// configure peer management behaviour
queue = {

@@ -77,3 +79,3 @@ // an array of backoff times, in millieconds

unresponsive: 7500,
// how long to wait before fogetting that a peer
// how long to wait before fogetting that a peer
// has been banned

@@ -124,3 +126,3 @@ banned: Infinity

#### `swarm.on('connection', (socket, details) => {})`
#### `swarm.on('connection', (socket, info) => {})`

@@ -130,3 +132,3 @@ A new connection has been created. You should handle this event by using the socket.

- `socket`. The established TCP or UTP socket.
- `details`. Object describing the connection.
- `info`. Object describing the connection.
- `type`. String. Should be either `'tcp'` or `'utp'`.

@@ -144,7 +146,29 @@ - `client`. Boolean. If true, the connection was initiated by this node.

- `topic`. Buffer. The identifier which this peer was discovered under.
The `details` argument is a `PeerInfo` object, which will emit events of the form `details.on('topic', topic => ...)` when the `multiplex` flag is `true`.
#### `swarm.on('disconnection', (socket, details) => {})`
#### `info.ban()`
Call this to ban this peer. Makes the swarm stop connecting to it.
#### `info.backoff()`
Call this to make the swarm backoff reconnecting to this peer.
Can be called multiple times to backoff more.
#### `dropped = info.deduplicate(localIdBuffer, remoteIdBuffer)`
Use this method to deduplicate connections.
When two swarms both announce and do lookups on the same topic you'll get duplicate connections
between them (one client connection and one server connection each).
If you exchange some sort of peer id between them you can use this method to make Hyperswarm
deduplicate those connection (ie drop one of them deterministically).
If it returns true then this current connection was dropped due to deduplication and is auto removed.
Only call this once per connection.
#### `swarm.on('disconnection', (socket, info) => {})`
A connection has been dropped.

@@ -170,4 +194,18 @@

#### `swarm.on('peer-rejected', (peer) => {})`
A peer has been rejected as a connection candidate.
- `peer`. Object describing the peer.
- `port`. Number.
- `host`. String. The IP address of the peer.
- `local`. Boolean. Is the peer on the LAN?
- `referrer`. Object. The address of the node that informed us of the peer.
- `port`. Number.
- `host`. String. The IP address of the referrer.
- `id`. Buffer.
- `topic`. Buffer. The identifier which this peer was discovered under.
#### `swarm.on('updated', ({ key }) => {})`
Emitted once a discovery cycle for a particular topic has completed. The topic can be identified by the `key` property of the emitted object. After this event the peer will wait for period of between 5 and 10 minutes before looking for new peers on that topic again.

@@ -32,2 +32,3 @@ 'use strict'

ephemeral,
validatePeer = () => true,
queue = {}

@@ -42,3 +43,3 @@ } = opts

socket: (socket, isTCP) => {
const info = peerInfo(null)
const info = peerInfo(null, this[kQueue])
info.connected(socket, isTCP)

@@ -73,2 +74,4 @@ this.emit('connection', socket, info)

this.validatePeer = validatePeer
this[kQueue] = peerQueue(queue)

@@ -159,2 +162,6 @@ this[kQueue].on('readable', this[kDrain](this[kQueue]))

topic.on('peer', (peer) => {
if (!this.validatePeer(peer)) {
this.emit('peer-rejected', peer)
return
}
this.emit('peer', peer)

@@ -161,0 +168,0 @@ this[kQueue].add(peer)

@@ -94,5 +94,11 @@ 'use strict'

const { peer: peer1 } = q.shift()
const { peer: peer2 } = q.shift()
let { peer: peer1 } = q.shift()
let { peer: peer2 } = q.shift()
if (peer1.topic.equals(Buffer.from('world'))) {
const tmp = peer2
peer2 = peer1
peer1 = tmp
}
same(peer1, { port: 8000, host: '127.0.0.1', topic: Buffer.from('hello') })

@@ -99,0 +105,0 @@ same(peer2, { port: 8000, host: '127.0.0.1', topic: Buffer.from('world') })

@@ -5,3 +5,3 @@ 'use strict'

const { NetworkResource } = require('@hyperswarm/network')
const { test, skip, only } = require('tap')
const { test } = require('tap')
const { once, done, promisifyMethod, whenifyMethod } = require('nonsynchronous')

@@ -13,3 +13,5 @@ const { dhtBootstrap, validSocket } = require('./util')

test('default ephemerality', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({
bootstrap: []
})
is(swarm.ephemeral, true)

@@ -23,3 +25,5 @@ promisifyMethod(swarm, 'listen')

test('destroyed property', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({
bootstrap: []
})
swarm.listen()

@@ -32,4 +36,7 @@ is(swarm.destroyed, false)

test('network property', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({
bootstrap: []
})
is(swarm.network instanceof NetworkResource, true)
swarm.destroy()
})

@@ -39,3 +46,4 @@

const swarm = hyperswarm({
ephemeral: false
ephemeral: false,
bootstrap: []
})

@@ -56,8 +64,7 @@ is(swarm.ephemeral, false)

is(swarm.network.discovery.dht.bootstrapNodes[0].port, port)
swarm.destroy()
closeDht()
closeDht(swarm)
})
test('emits listening event when bound', async ({ pass }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
swarm.listen()

@@ -70,3 +77,3 @@ await once(swarm, 'listening')

test('emits close event when destroyed', async ({ pass }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
promisifyMethod(swarm, 'listen')

@@ -80,3 +87,3 @@ await swarm.listen()

test('join - missing key', async ({ throws }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
promisifyMethod(swarm, 'listen')

@@ -90,3 +97,3 @@ await swarm.listen()

test('join automatically binds', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
var bind = false

@@ -100,3 +107,3 @@ swarm.network.bind = () => (bind = true)

test('join – emits error event when failing to bind', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const fauxError = Error('problem binding')

@@ -111,3 +118,3 @@ swarm.network.bind = (cb) => process.nextTick(cb, fauxError)

test('join – default options', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
var lookupKey = null

@@ -126,3 +133,3 @@ const key = Buffer.from('key')

test('join - announce: false, lookup: true', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
var lookupKey = null

@@ -141,3 +148,3 @@ const key = Buffer.from('key')

test('join - announce: false, lookup: false', async ({ throws }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const key = Buffer.from('key')

@@ -152,3 +159,3 @@ throws(

test('join - emits update event when topic updates', async ({ pass }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const key = Buffer.from('key')

@@ -165,4 +172,6 @@ const topic = new EventEmitter()

test('join - emits peer event when topic recieves peer', async ({ pass, is }) => {
const swarm = hyperswarm()
test('join - emits peer event when topic recieves peer', async ({ plan, pass, is }) => {
plan(2)
const swarm = hyperswarm({ bootstrap: [] })
const key = Buffer.from('key')

@@ -174,13 +183,14 @@ const topic = new EventEmitter()

await once(swarm, 'listening')
process.nextTick(() => {
topic.emit('peer', fauxPeer)
swarm.once('peer', function (peer) {
pass('event emitted')
is(peer, fauxPeer)
swarm.destroy()
})
const [ peer ] = await once(swarm, 'peer')
pass('event emitted')
is(peer, fauxPeer)
swarm.destroy()
topic.emit('peer', fauxPeer)
})
test('join - announce: true, lookup: false', async ({ is, fail }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
var announceKey = null

@@ -206,4 +216,6 @@ const key = Buffer.from('key')

test('join - announce: true, lookup: true', async ({ is }) => {
const swarm = hyperswarm()
test('join - announce: true, lookup: true', async ({ plan, is }) => {
plan(2)
const swarm = hyperswarm({ bootstrap: [] })
var announceKey = null

@@ -215,2 +227,3 @@ const key = Buffer.from('key')

announceKey = key
topic.key = key
return topic

@@ -220,13 +233,14 @@ }

await once(swarm, 'listening')
process.nextTick(() => {
topic.emit('peer', fauxPeer)
swarm.once('peer', function (peer) {
is(peer, fauxPeer)
is(announceKey, key)
swarm.destroy()
})
const [ peer ] = await once(swarm, 'peer')
is(peer, fauxPeer)
is(announceKey, key)
swarm.destroy()
topic.emit('peer', fauxPeer)
})
test('leave - missing key', async ({ throws }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
promisifyMethod(swarm, 'listen')

@@ -240,3 +254,3 @@ await swarm.listen()

test('leave destroys the topic for a given pre-existing key', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const key = Buffer.concat([Buffer.alloc(28), Buffer.from('key1')])

@@ -265,3 +279,3 @@ const key2 = Buffer.concat([Buffer.alloc(28), Buffer.from('key2')])

test('leave does not throw when a given key was never joined', async ({ doesNotThrow }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const key = Buffer.from('key1')

@@ -276,3 +290,3 @@ const key2 = Buffer.from('key2')

test('joining the same topic twice will leave the topic before rejoining', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const key = Buffer.from('key')

@@ -299,3 +313,3 @@ const { lookup } = swarm.network

test('connect to a swarm with a plain TCP client', async ({ pass, same, is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
promisifyMethod(swarm, 'listen')

@@ -324,4 +338,4 @@ await swarm.listen()

test('connect two peers directly', async ({ is }) => {
const swarm1 = hyperswarm()
const swarm2 = hyperswarm()
const swarm1 = hyperswarm({ bootstrap: [] })
const swarm2 = hyperswarm({ bootstrap: [] })
swarm1.listen()

@@ -371,5 +385,3 @@ await once(swarm1, 'listening')

peer2.leave(key)
peer1.destroy()
peer2.destroy()
closeDht()
closeDht(peer1, peer2)
})

@@ -401,5 +413,3 @@

peer2.leave(key)
peer1.destroy()
peer2.destroy()
closeDht()
closeDht(peer1, peer2)
})

@@ -431,5 +441,3 @@

peer2.leave(key)
peer1.destroy()
peer2.destroy()
closeDht()
closeDht(peer1, peer2)
})

@@ -459,5 +467,3 @@

swarm2.leave(key)
swarm1.destroy()
swarm2.destroy()
closeDht()
closeDht(swarm1, swarm2)
})

@@ -486,5 +492,3 @@

swarm2.leave(key)
swarm1.destroy()
swarm2.destroy()
closeDht()
closeDht(swarm1, swarm2)
})

@@ -516,4 +520,3 @@

swarm2.leave(key)
swarm2.destroy()
closeDht()
closeDht(swarm2)
})

@@ -546,4 +549,3 @@

swarm1.leave(key)
swarm1.destroy()
closeDht()
closeDht(swarm1)
})

@@ -577,7 +579,6 @@

swarm2.leave(key)
swarm2.destroy()
closeDht()
closeDht(swarm2)
})
only('can multiplex 100 topics over the same connection', async ({ same }) => {
test('can multiplex 100 topics over the same connection', async ({ same }) => {
const { bootstrap, closeDht } = await dhtBootstrap()

@@ -601,3 +602,3 @@ const swarm1 = hyperswarm({ bootstrap, maxPeers: 20, queue: { multiplex: true } })

// Start listening for new connections, and briefly wait to flush the DHT.
const emittedTopicsPromise = listenForConnections(swarm2)
const l = listenForConnections(swarm2)
await new Promise(resolve => setTimeout(resolve, 100))

@@ -614,6 +615,2 @@

const topicSet = new Set(topics.map(t => t.toString('hex')))
const failTimer = setTimeout(() => {
throw new Error('Did not establish connections in time.')
}, 5000)
const emittedTopics = await emittedTopicsPromise

@@ -625,14 +622,14 @@ for (const topic of topics) {

same(topicSet.size, 0)
await l
for (const topic of topics) {
swarm1.leave(topic)
swarm2.leave(topic)
}
swarm1.destroy()
swarm2.destroy()
closeDht()
closeDht(swarm1, swarm2)
function listenForConnections (swarm) {
const emittedTopics = []
return new Promise(resolve => {
return new Promise((resolve, reject) => {
const failTimer = setTimeout(() => {
reject(new Error('Did not establish connections in time.'))
}, 5000)
swarm.on('connection', (socket, info) => {

@@ -642,8 +639,10 @@ for (let topic of info.topics) {

}
info.on('topic', topic => {
pushTopic(topic)
})
function pushTopic (topic) {
emittedTopics.push(topic)
if (topics.length === numTopics) {
if (emittedTopics.length === numTopics) {
clearTimeout(failTimer)

@@ -658,1 +657,34 @@ info.removeAllListeners('topic')

})
test('can dedup connections', async ({ same, end }) => {
const { bootstrap, closeDht } = await dhtBootstrap()
const swarm1 = hyperswarm({ bootstrap, maxPeers: 20, queue: { multiplex: true } })
const swarm2 = hyperswarm({ bootstrap, maxPeers: 20, queue: { multiplex: true } })
swarm1.on('connection', (socket, info) => {
socket.write('b')
socket.once('data', function (id) {
info.deduplicate(Buffer.from('b'), id)
})
socket.on('error', () => {})
})
swarm2.on('connection', (socket, info) => {
socket.write('a')
socket.once('data', function (id) {
info.deduplicate(Buffer.from('a'), id)
})
socket.on('error', () => {})
})
const topic = randomBytes(32)
swarm1.join(topic, { announce: true, lookup: true })
swarm2.join(topic, { announce: true, lookup: true })
await new Promise(resolve => setTimeout(resolve, 100))
same(swarm1.connections.size, 1)
same(swarm1.connections.size, 1)
closeDht(swarm1, swarm2)
})

@@ -9,3 +9,3 @@ 'use strict'

test('maxClientSockets defaults to Infinity', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const { maxClientSockets } = swarm

@@ -61,5 +61,4 @@ is(maxClientSockets, Infinity)

s.leave(key)
s.destroy()
}
closeDht()
closeDht(...swarms)
})

@@ -112,5 +111,4 @@

s.leave(key)
s.destroy()
}
closeDht()
closeDht(...swarms)
})

@@ -9,3 +9,3 @@ 'use strict'

test('maxPeers defaults to 24', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const { maxPeers } = swarm

@@ -60,10 +60,7 @@ is(maxPeers, 24)

is(swarm.open, false)
swarm2.destroy()
swarm.leave(key)
swarm.destroy()
for (const s of swarms) {
s.leave(key)
s.destroy()
}
closeDht()
closeDht(swarm, swarm2, ...swarms)
})

@@ -112,10 +109,7 @@

is(swarm.open, false)
swarm2.destroy()
swarm.leave(key)
swarm.destroy()
for (const s of swarms) {
s.leave(key)
s.destroy()
}
closeDht()
closeDht(swarm, swarm2, ...swarms)
})

@@ -186,10 +180,7 @@

is(swarm.open, false)
swarm2.destroy()
swarm.leave(key)
swarm.destroy()
for (const s of swarms) {
s.leave(key)
s.destroy()
}
closeDht()
closeDht(swarm, swarm2, ...swarms)
})

@@ -263,10 +254,7 @@

swarm2.destroy()
swarm.leave(key)
swarm.destroy()
for (const s of swarms) {
s.leave(key)
s.destroy()
}
closeDht()
closeDht(swarm, swarm2, ...swarms)
})

@@ -89,5 +89,4 @@ 'use strict'

s.leave(key)
s.destroy()
}
closeDht()
closeDht(...swarms)
})
'use strict'
const { EventEmitter } = require('events')
const { randomBytes } = require('crypto')

@@ -11,3 +10,3 @@ const { test } = require('tap')

test('maxServerSockets defaults to Infinity', async ({ is }) => {
const swarm = hyperswarm()
const swarm = hyperswarm({ bootstrap: [] })
const { maxServerSockets } = swarm

@@ -49,10 +48,7 @@ is(maxServerSockets, Infinity)

await timeout(150) // allow time for a potential connection event
swarm2.destroy()
swarm.leave(key)
swarm.destroy()
for (const s of swarms) {
s.leave(key)
s.destroy()
}
closeDht()
closeDht(swarm, swarm2, ...swarms)
})

@@ -102,10 +98,7 @@

swarm2.destroy()
swarm.leave(key)
swarm.destroy()
for (const s of swarms) {
s.leave(key)
s.destroy()
}
closeDht()
closeDht(swarm, swarm2, ...swarms)
})

@@ -141,2 +134,3 @@

is(maxServerSockets, 4)
swarm.on('connection', c => c.write('hi'))
swarm.join(key, {

@@ -159,24 +153,5 @@ announce: true,

// fake utp connection
s.network.utp.connect = (port, host) => {
const ee = new EventEmitter()
ee.destroy = (cb) => {
if (cb) ee.once('close', cb)
ee.emit('close')
}
// filter out multiple connect attempts
if (host === '127.0.0.1') return ee
process.nextTick(() => {
ee.emit('connect')
const conn = new EventEmitter()
conn.destroy = (cb) => {
if (cb) ee.once('close', cb)
ee.emit('close')
}
swarm.network.utp.emit('connection', conn)
})
return ee
}
await once(swarm, 'connection')
}
is(swarm.serverSockets, maxServerSockets)

@@ -189,3 +164,2 @@

hyperswarm = require('../swarm')
for (var c = 0; c < maxServerSockets; c++) {

@@ -211,3 +185,4 @@ const s = hyperswarm({ bootstrap })

await once(swarm2, 'listening')
swarm.once('connection', () => {
swarm.once('connection', (_, info) => {
fail('connection should not be emitted after double max server connections is reached')

@@ -218,10 +193,9 @@ })

swarm2.leave(key)
swarm2.destroy()
swarm.leave(key)
swarm.destroy()
for (const s of swarms) {
s.leave(key)
s.destroy()
}
closeDht()
closeDht(swarm, swarm2, ...swarms)
setImmediate(() => process.exit(0)) // haxx exit the process so we don't have to wait for utp timeouts ... maybe something we can improve in utp?
})

@@ -8,3 +8,7 @@ 'use strict'

async function dhtBootstrap () {
const node = dht()
const node = dht({
bootstrap: [],
ephemeral: true
})
node.listen()
await once(node, 'listening')

@@ -15,3 +19,16 @@ const { port } = node.address()

bootstrap: [`127.0.0.1:${port}`],
closeDht: () => node.destroy()
closeDht (...others) {
let missing = 1
for (const n of others) {
missing++
n.destroy(done)
}
done()
function done () {
if (--missing) return
node.destroy()
}
}
}

@@ -18,0 +35,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc