mirror of
https://github.com/fluencelabs/js-libp2p-interfaces
synced 2025-04-24 16:52:22 +00:00
feat: timeline and close checking (#55)
* feat: add test to validate timeline presence * feat: add test and docs for close and timeline * feat: add filter test * docs: dont reference go, it's not relevant
This commit is contained in:
parent
02fe6d9040
commit
993ca1cb85
29
README.md
29
README.md
@ -17,8 +17,6 @@ Publishing a test suite as a module lets multiple modules all ensure compatibili
|
||||
|
||||
The purpose of this interface is not to reinvent any wheels when it comes to dialing and listening to transports. Instead, it tries to provide a uniform API for several transports through a shimmed interface.
|
||||
|
||||
The API is presented with both Node.js and Go primitives, however there are no actual limitations for it to be extended for any other language, pushing forward the cross compatibility and interop through diferent stacks.
|
||||
|
||||
## Lead Maintainer
|
||||
|
||||
[Jacob Heun](https://github.com/jacobheun/)
|
||||
@ -93,6 +91,7 @@ A valid transport (one that follows the interface defined) must implement the fo
|
||||
- type: `Transport`
|
||||
- `new Transport({ upgrader, ...[options] })`
|
||||
- `<Promise> transport.dial(multiaddr, [options])`
|
||||
- `<Multiaddr[]> transport.filter(multiaddrs)`
|
||||
- `transport.createListener([options], handlerFunction)`
|
||||
- type: `transport.Listener`
|
||||
- event: 'listening'
|
||||
@ -122,12 +121,17 @@ The `Upgrader` methods take a [MultiaddrConnection](#multiaddrconnection) and wi
|
||||
- `MultiaddrConnection`
|
||||
- `sink<function(source)>`: A [streaming iterable sink](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#sink-it)
|
||||
- `source<AsyncIterator>`: A [streaming iterable source](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#source-it)
|
||||
- `close<function(Error)>`: A method for closing the connection
|
||||
- `conn`: The raw connection of the transport, such as a TCP socket.
|
||||
- `remoteAddr<Multiaddr>`: The remote `Multiaddr` of the connection.
|
||||
- `[localAddr<Multiaddr>]`: An optional local `Multiaddr` of the connection.
|
||||
- `timeline<object>`: A hash map of connection time events
|
||||
- `open<number>`: The time in ticks the connection was opened
|
||||
- `close<number>`: The time in ticks the connection was closed
|
||||
|
||||
### Creating a transport instance
|
||||
|
||||
- `JavaScript` - `const transport = new Transport({ upgrader, ...[options] })`
|
||||
- `const transport = new Transport({ upgrader, ...[options] })`
|
||||
|
||||
Creates a new Transport instance. `options` is an JavaScript object that should include the necessary parameters for the transport instance. Options **MUST** include an `Upgrader` instance, as Transports will use this to return `interface-connection` instances from `transport.dial` and the listener `handlerFunction`.
|
||||
|
||||
@ -135,7 +139,7 @@ Creates a new Transport instance. `options` is an JavaScript object that should
|
||||
|
||||
### Dial to another peer
|
||||
|
||||
- `JavaScript` - `const connection = await transport.dial(multiaddr, [options])`
|
||||
- `const connection = await transport.dial(multiaddr, [options])`
|
||||
|
||||
This method uses a transport to dial a Peer listening on `multiaddr`.
|
||||
|
||||
@ -172,9 +176,18 @@ try {
|
||||
// ----
|
||||
```
|
||||
|
||||
### Filtering Addresses
|
||||
|
||||
- `const supportedAddrs = await transport.filter(multiaddrs)`
|
||||
|
||||
When using a transport its important to be able to filter out `multiaddr`s that the transport doesn't support. A transport instance provides a filter method to return only the valid addresses it supports.
|
||||
|
||||
`multiaddrs` must be an array of type [`multiaddr`](https://www.npmjs.com/multiaddr).
|
||||
Filter returns an array of `multiaddr`.
|
||||
|
||||
### Create a listener
|
||||
|
||||
- `JavaScript` - `const listener = transport.createListener([options], handlerFunction)`
|
||||
- `const listener = transport.createListener([options], handlerFunction)`
|
||||
|
||||
This method creates a listener on the transport. Implementations **MUST** call `upgrader.upgradeInbound(multiaddrConnection)` and pass its results to the `handlerFunction` and any emitted `connection` events.
|
||||
|
||||
@ -191,7 +204,7 @@ The listener object created may emit the following events:
|
||||
|
||||
### Start a listener
|
||||
|
||||
- `JavaScript` - `await listener.listen(multiaddr)`
|
||||
- `await listener.listen(multiaddr)`
|
||||
|
||||
This method puts the listener in `listening` mode, waiting for incoming connections.
|
||||
|
||||
@ -199,13 +212,13 @@ This method puts the listener in `listening` mode, waiting for incoming connecti
|
||||
|
||||
### Get listener addrs
|
||||
|
||||
- `JavaScript` - `listener.getAddrs()`
|
||||
- `listener.getAddrs()`
|
||||
|
||||
This method returns the addresses on which this listener is listening. Useful when listening on port 0 or any interface (0.0.0.0).
|
||||
|
||||
### Stop a listener
|
||||
|
||||
- `JavaScript` - `await listener.close([options])`
|
||||
- `await listener.close([options])`
|
||||
|
||||
This method closes the listener so that no more connections can be opened on this transport instance.
|
||||
|
||||
|
@ -6,6 +6,7 @@ const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
|
||||
const { isValidTick } = require('./utils')
|
||||
const goodbye = require('it-goodbye')
|
||||
const { collect } = require('streaming-iterables')
|
||||
const pipe = require('it-pipe')
|
||||
@ -15,19 +16,18 @@ const sinon = require('sinon')
|
||||
|
||||
module.exports = (common) => {
|
||||
const upgrader = {
|
||||
upgradeOutbound (multiaddrConnection) {
|
||||
['sink', 'source', 'remoteAddr', 'conn'].forEach(prop => {
|
||||
_upgrade (multiaddrConnection) {
|
||||
['sink', 'source', 'remoteAddr', 'conn', 'timeline', 'close'].forEach(prop => {
|
||||
expect(multiaddrConnection).to.have.property(prop)
|
||||
})
|
||||
|
||||
return { sink: multiaddrConnection.sink, source: multiaddrConnection.source }
|
||||
expect(isValidTick(multiaddrConnection.timeline.open)).to.equal(true)
|
||||
return multiaddrConnection
|
||||
},
|
||||
upgradeOutbound (multiaddrConnection) {
|
||||
return upgrader._upgrade(multiaddrConnection)
|
||||
},
|
||||
upgradeInbound (multiaddrConnection) {
|
||||
['sink', 'source', 'remoteAddr', 'conn'].forEach(prop => {
|
||||
expect(multiaddrConnection).to.have.property(prop)
|
||||
})
|
||||
|
||||
return { sink: multiaddrConnection.sink, source: multiaddrConnection.source }
|
||||
return upgrader._upgrade(multiaddrConnection)
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +67,16 @@ module.exports = (common) => {
|
||||
expect(result[0].toString()).to.equal('hey')
|
||||
})
|
||||
|
||||
it('can close connections', async () => {
|
||||
const upgradeSpy = sinon.spy(upgrader, 'upgradeOutbound')
|
||||
const conn = await transport.dial(addrs[0])
|
||||
|
||||
expect(upgradeSpy.callCount).to.equal(1)
|
||||
expect(upgradeSpy.returned(conn)).to.equal(true)
|
||||
await conn.close()
|
||||
expect(isValidTick(conn.timeline.close)).to.equal(true)
|
||||
})
|
||||
|
||||
it('to non existent listener', async () => {
|
||||
const upgradeSpy = sinon.spy(upgrader, 'upgradeOutbound')
|
||||
try {
|
||||
|
37
src/filter-test.js
Normal file
37
src/filter-test.js
Normal file
@ -0,0 +1,37 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
|
||||
module.exports = (common) => {
|
||||
const upgrader = {
|
||||
_upgrade (multiaddrConnection) {
|
||||
return multiaddrConnection
|
||||
},
|
||||
upgradeOutbound (multiaddrConnection) {
|
||||
return upgrader._upgrade(multiaddrConnection)
|
||||
},
|
||||
upgradeInbound (multiaddrConnection) {
|
||||
return upgrader._upgrade(multiaddrConnection)
|
||||
}
|
||||
}
|
||||
|
||||
describe('filter', () => {
|
||||
let addrs
|
||||
let transport
|
||||
|
||||
before(async () => {
|
||||
({ addrs, transport } = await common.setup({ upgrader }))
|
||||
})
|
||||
|
||||
after(() => common.teardown && common.teardown())
|
||||
|
||||
it('filters addresses', () => {
|
||||
const filteredAddrs = transport.filter(addrs)
|
||||
expect(filteredAddrs).to.eql(addrs)
|
||||
})
|
||||
})
|
||||
}
|
@ -3,11 +3,13 @@
|
||||
|
||||
const dial = require('./dial-test')
|
||||
const listen = require('./listen-test')
|
||||
const filter = require('./filter-test')
|
||||
|
||||
module.exports = (common) => {
|
||||
describe('interface-transport', () => {
|
||||
dial(common)
|
||||
listen(common)
|
||||
filter(common)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -9,22 +9,23 @@ chai.use(dirtyChai)
|
||||
const sinon = require('sinon')
|
||||
|
||||
const pipe = require('it-pipe')
|
||||
const { isValidTick } = require('./utils')
|
||||
|
||||
module.exports = (common) => {
|
||||
const upgrader = {
|
||||
upgradeOutbound (multiaddrConnection) {
|
||||
['sink', 'source', 'remoteAddr', 'conn'].forEach(prop => {
|
||||
_upgrade (multiaddrConnection) {
|
||||
['sink', 'source', 'remoteAddr', 'conn', 'timeline', 'close'].forEach(prop => {
|
||||
expect(multiaddrConnection).to.have.property(prop)
|
||||
})
|
||||
expect(isValidTick(multiaddrConnection.timeline.open)).to.equal(true)
|
||||
|
||||
return { sink: multiaddrConnection.sink, source: multiaddrConnection.source }
|
||||
return multiaddrConnection
|
||||
},
|
||||
upgradeOutbound (multiaddrConnection) {
|
||||
return upgrader._upgrade(multiaddrConnection)
|
||||
},
|
||||
upgradeInbound (multiaddrConnection) {
|
||||
['sink', 'source', 'remoteAddr', 'conn'].forEach(prop => {
|
||||
expect(multiaddrConnection).to.have.property(prop)
|
||||
})
|
||||
|
||||
return { sink: multiaddrConnection.sink, source: multiaddrConnection.source }
|
||||
return upgrader._upgrade(multiaddrConnection)
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,8 +51,10 @@ module.exports = (common) => {
|
||||
|
||||
it('close listener with connections, through timeout', async () => {
|
||||
const upgradeSpy = sinon.spy(upgrader, 'upgradeInbound')
|
||||
const listenerConns = []
|
||||
|
||||
const listener = transport.createListener((conn) => {
|
||||
listenerConns.push(conn)
|
||||
expect(upgradeSpy.returned(conn)).to.equal(true)
|
||||
pipe(conn, conn)
|
||||
})
|
||||
@ -78,6 +81,13 @@ module.exports = (common) => {
|
||||
listener.close()
|
||||
])
|
||||
|
||||
await socket1.close()
|
||||
|
||||
expect(isValidTick(socket1.timeline.close)).to.equal(true)
|
||||
listenerConns.forEach(conn => {
|
||||
expect(isValidTick(conn.timeline.close)).to.equal(true)
|
||||
})
|
||||
|
||||
// 2 dials = 2 connections upgraded
|
||||
expect(upgradeSpy.callCount).to.equal(2)
|
||||
})
|
||||
|
16
src/utils/index.js
Normal file
16
src/utils/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* A tick is considered valid if it happened between now
|
||||
* and `ms` milliseconds ago
|
||||
* @param {number} date Time in ticks
|
||||
* @param {number} ms max milliseconds that should have expired
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isValidTick: function isValidTick (date, ms = 5000) {
|
||||
const now = Date.now()
|
||||
if (date > now - ms && date <= now) return true
|
||||
return false
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user