From af7783844610d7aaf9f34cb29c883b72aeaa91ca Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Tue, 17 Sep 2019 17:02:50 +0200 Subject: [PATCH 1/4] feat: add test to validate timeline presence --- src/dial-test.js | 7 +++++-- src/listen-test.js | 7 +++++-- src/utils/index.js | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 src/utils/index.js diff --git a/src/dial-test.js b/src/dial-test.js index 80d29b6..56a9638 100644 --- a/src/dial-test.js +++ b/src/dial-test.js @@ -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') @@ -16,16 +17,18 @@ const sinon = require('sinon') module.exports = (common) => { const upgrader = { upgradeOutbound (multiaddrConnection) { - ['sink', 'source', 'remoteAddr', 'conn'].forEach(prop => { + ['sink', 'source', 'remoteAddr', 'conn', 'timeline'].forEach(prop => { expect(multiaddrConnection).to.have.property(prop) }) + expect(isValidTick(multiaddrConnection.timeline.open)).to.equal(true) return { sink: multiaddrConnection.sink, source: multiaddrConnection.source } }, upgradeInbound (multiaddrConnection) { - ['sink', 'source', 'remoteAddr', 'conn'].forEach(prop => { + ['sink', 'source', 'remoteAddr', 'conn', 'timeline'].forEach(prop => { expect(multiaddrConnection).to.have.property(prop) }) + expect(isValidTick(multiaddrConnection.timeline.open)).to.equal(true) return { sink: multiaddrConnection.sink, source: multiaddrConnection.source } } diff --git a/src/listen-test.js b/src/listen-test.js index 9754cbd..cb1495d 100644 --- a/src/listen-test.js +++ b/src/listen-test.js @@ -9,20 +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 => { + ['sink', 'source', 'remoteAddr', 'conn', 'timeline'].forEach(prop => { expect(multiaddrConnection).to.have.property(prop) }) + expect(isValidTick(multiaddrConnection.timeline.open)).to.equal(true) return { sink: multiaddrConnection.sink, source: multiaddrConnection.source } }, upgradeInbound (multiaddrConnection) { - ['sink', 'source', 'remoteAddr', 'conn'].forEach(prop => { + ['sink', 'source', 'remoteAddr', 'conn', 'timeline'].forEach(prop => { expect(multiaddrConnection).to.have.property(prop) }) + expect(isValidTick(multiaddrConnection.timeline.open)).to.equal(true) return { sink: multiaddrConnection.sink, source: multiaddrConnection.source } } diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..b0f3bf1 --- /dev/null +++ b/src/utils/index.js @@ -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 + } +} From 314c99c71a0090595351bca2cd88166e07870444 Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 18 Sep 2019 12:31:30 +0200 Subject: [PATCH 2/4] feat: add test and docs for close and timeline --- README.md | 5 +++++ src/dial-test.js | 27 +++++++++++++++++---------- src/listen-test.js | 25 ++++++++++++++++--------- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 2c44362..63173a6 100644 --- a/README.md +++ b/README.md @@ -122,8 +122,13 @@ The `Upgrader` methods take a [MultiaddrConnection](#multiaddrconnection) and wi - `MultiaddrConnection` - `sink`: A [streaming iterable sink](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#sink-it) - `source`: A [streaming iterable source](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#source-it) + - `close`: A method for closing the connection - `conn`: The raw connection of the transport, such as a TCP socket. - `remoteAddr`: The remote `Multiaddr` of the connection. + - `[localAddr]`: An optional local `Multiaddr` of the connection. + - `timeline`: A hash map of connection time events + - `open`: The time in ticks the connection was opened + - `close`: The time in ticks the connection was closed ### Creating a transport instance diff --git a/src/dial-test.js b/src/dial-test.js index 56a9638..d6f919c 100644 --- a/src/dial-test.js +++ b/src/dial-test.js @@ -16,21 +16,18 @@ const sinon = require('sinon') module.exports = (common) => { const upgrader = { - upgradeOutbound (multiaddrConnection) { - ['sink', 'source', 'remoteAddr', 'conn', 'timeline'].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', 'timeline'].forEach(prop => { - expect(multiaddrConnection).to.have.property(prop) - }) - expect(isValidTick(multiaddrConnection.timeline.open)).to.equal(true) - - return { sink: multiaddrConnection.sink, source: multiaddrConnection.source } + return upgrader._upgrade(multiaddrConnection) } } @@ -70,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 { diff --git a/src/listen-test.js b/src/listen-test.js index cb1495d..a455195 100644 --- a/src/listen-test.js +++ b/src/listen-test.js @@ -13,21 +13,19 @@ const { isValidTick } = require('./utils') module.exports = (common) => { const upgrader = { - upgradeOutbound (multiaddrConnection) { - ['sink', 'source', 'remoteAddr', 'conn', 'timeline'].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', 'timeline'].forEach(prop => { - expect(multiaddrConnection).to.have.property(prop) - }) - expect(isValidTick(multiaddrConnection.timeline.open)).to.equal(true) - - return { sink: multiaddrConnection.sink, source: multiaddrConnection.source } + return upgrader._upgrade(multiaddrConnection) } } @@ -53,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) }) @@ -81,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) }) From 64068d3eeccde392858e38c4b32aee8c68b5b0ea Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 18 Sep 2019 12:32:35 +0200 Subject: [PATCH 3/4] feat: add filter test --- README.md | 10 ++++++++++ src/filter-test.js | 37 +++++++++++++++++++++++++++++++++++++ src/index.js | 2 ++ 3 files changed, 49 insertions(+) create mode 100644 src/filter-test.js diff --git a/README.md b/README.md index 63173a6..3dec90f 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ A valid transport (one that follows the interface defined) must implement the fo - type: `Transport` - `new Transport({ upgrader, ...[options] })` - ` transport.dial(multiaddr, [options])` + - ` transport.filter(multiaddrs)` - `transport.createListener([options], handlerFunction)` - type: `transport.Listener` - event: 'listening' @@ -177,6 +178,15 @@ try { // ---- ``` +### Filtering Addresses + +- `JavaScript` - `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)` diff --git a/src/filter-test.js b/src/filter-test.js new file mode 100644 index 0000000..09c53a3 --- /dev/null +++ b/src/filter-test.js @@ -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) + }) + }) +} diff --git a/src/index.js b/src/index.js index 73f1373..ba9ee98 100644 --- a/src/index.js +++ b/src/index.js @@ -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) }) } From 82bcb46576e8a54479c76d3ad036d9770aa031cb Mon Sep 17 00:00:00 2001 From: Jacob Heun Date: Wed, 18 Sep 2019 13:03:16 +0200 Subject: [PATCH 4/4] docs: dont reference go, it's not relevant --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3dec90f..5c7fe49 100644 --- a/README.md +++ b/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/) @@ -133,7 +131,7 @@ The `Upgrader` methods take a [MultiaddrConnection](#multiaddrconnection) and wi ### 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`. @@ -141,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`. @@ -180,7 +178,7 @@ try { ### Filtering Addresses -- `JavaScript` - `const supportedAddrs = await transport.filter(multiaddrs)` +- `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. @@ -189,7 +187,7 @@ 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. @@ -206,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. @@ -214,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.