Skip to content
This repository has been archived by the owner on Mar 11, 2020. It is now read-only.

refactor: API changes and switch to async iterators #29

Merged
merged 27 commits into from
Sep 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bff922a
refactor: use async iterators
vasco-santos Apr 22, 2019
3a992f3
chore: apply suggestions from code review
dirkmc Apr 23, 2019
4ae3ff4
chore: code review feedback
vasco-santos Apr 23, 2019
09f3b15
chore: apply suggestions from code review
Apr 23, 2019
be25035
chore: code review feedback
vasco-santos Apr 23, 2019
2b0d1d8
refactor: new approach
vasco-santos May 22, 2019
50295a9
chore: apply suggestions from code review
vasco-santos May 23, 2019
b3f9aa6
chore: add socket concept
vasco-santos May 28, 2019
fd0d1e9
chore: address review
vasco-santos Jul 15, 2019
0e62793
chore: remove peerInfo from constructor
vasco-santos Jul 22, 2019
1f9e596
chore: extend class instead of pipe
vasco-santos Jul 22, 2019
81e725c
chore: use newest err-code
vasco-santos Aug 7, 2019
a636b0d
chore: improve readability
vasco-santos Aug 7, 2019
3fca0c8
chore: rename stream sink function
vasco-santos Aug 27, 2019
37e8ab7
chore: apply suggestions from code review
vasco-santos Sep 8, 2019
84ca22a
chore: address review
vasco-santos Sep 8, 2019
b039aec
chore: apply suggestions from code review
vasco-santos Sep 13, 2019
77c0121
chore: address review
vasco-santos Sep 16, 2019
0c25138
feat: use compliance tests on self
jacobheun Sep 17, 2019
8671d50
chore: update docs
vasco-santos Sep 17, 2019
aca147f
chore: add tests to connection
vasco-santos Sep 17, 2019
7164204
chore: address review
vasco-santos Sep 17, 2019
ff3c3b9
fix: remove closed streams from connection streams array
vasco-santos Sep 18, 2019
7310a84
chore: addstream function instead of onNewStream
vasco-santos Sep 18, 2019
6f920a6
chore: use stream registry map
vasco-santos Sep 18, 2019
8639867
fix: remove stream export
jacobheun Sep 23, 2019
15f000c
chore: apply suggestions from code review
vasco-santos Sep 23, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,39 @@ language: node_js
cache: npm
stages:
- check
- test
- cov

node_js:
- '10'
- '12'

os:
- linux
- osx
- windows

script: npx nyc -s npm run test:node -- --bail
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov

jobs:
include:
- stage: check
script:
- npx aegir commitlint --travis
- npx aegir dep-check
- npm run lint

- stage: test
name: chrome
addons:
chrome: stable
script: npx aegir test -t browser -t webworker

- stage: test
name: firefox
addons:
firefox: latest
script: npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless

notifications:
email: false
272 changes: 212 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,101 +10,253 @@ interface-connection
[![Dependency Status](https://david-dm.org/libp2p/interface-connection.svg?style=flat-square)](https://david-dm.org/libp2p/interface-connection)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)

This is a test suite and interface you can use to implement a connection. A connection is understood as something that offers mechanism for writing and reading data, back pressure, half and full duplex streams. This module and test suite were heavily inspired by abstract-blob-store and interface-stream-muxer.
This is a test suite and interface you can use to implement a connection. The connection interface contains all the metadata associated with it, as well as an array of the streams opened through this connection. In the same way as the connection, a stream contains properties with its metadata, plus an iterable duplex object that offers a mechanism for writing and reading data, with back pressure. This module and test suite were heavily inspired by abstract-blob-store and interface-stream-muxer.

The primary goal of this module is to enable developers to pick, swap or upgrade their connection without losing the same API expectations and mechanisms such as back pressure and the ability to half close a connection.

Publishing a test suite as a module lets multiple modules all ensure compatibility since they use the same test suite.

The API is presented with both Node.js and Go primitives, however, there is no actual limitations for it to be extended to any other language, pushing forward the cross compatibility and interop through diferent stacks.
Publishing a test suite as a module lets multiple modules ensure compatibility since they use the same test suite.

## Lead Maintainer

[Jacob Heun](https://github.com/jacobheun/)

# Modules that implement the interface
## Usage

- [js-libp2p-tcp](https://github.com/libp2p/js-libp2p-tcp)
- [js-libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star)
- [js-libp2p-websockets](https://github.com/libp2p/js-libp2p-websockets)
- [js-libp2p-utp](https://github.com/libp2p/js-libp2p-utp)
- [webrtc-explorer](https://github.com/diasdavid/webrtc-explorer)
- [js-libp2p-spdy](https://github.com/libp2p/js-libp2p-spdy)
- [js-libp2p-multiplex](https://github.com/libp2p/js-libp2p-multiplex)
- [js-libp2p-secio](https://github.com/libp2p/js-libp2p-secio)
### Connection

# Badge
Before creating a connection from a transport compatible with `libp2p` it is important to understand some concepts:

Include this badge in your readme if you make a module that is compatible with the interface-connection API. You can validate this by running the tests.
- **socket**: the underlying raw duplex connection between two nodes. It is created by the transports during a dial/listen.
vasco-santos marked this conversation as resolved.
Show resolved Hide resolved
- **[multiaddr connection](https://github.com/libp2p/interface-transport#multiaddrconnection)**: an abstraction over the socket to allow it to work with multiaddr addresses. It is a duplex connection that transports create to wrap the socket before passing to an upgrader that turns it into a standard connection (see below).
- **connection**: a connection between two _peers_ that has built in multiplexing and info about the connected peer. It is created from a [multiaddr connection](https://github.com/libp2p/interface-transport#multiaddrconnection) by an upgrader. The upgrader uses multistream-select to add secio and multiplexing and returns this object.
- **stream**: a muxed duplex channel of the `connection`. Each connection may have many streams.

![](https://raw.githubusercontent.com/diasdavid/interface-connection/master/img/badge.png)
A connection stands for the libp2p communication duplex layer between two nodes. It is **not** the underlying raw transport duplex layer (socket), such as a TCP socket, but an abstracted layer that sits on top of the raw socket.

# How to use the battery of tests
This helps ensuring that the transport is responsible for socket management, while also allowing the application layer to handle the connection management.

## Node.js
### Test suite

```
var tape = require('tape')
var tests = require('interface-connection/tests')
var YourConnectionHandler = require('../src')

var common = {
setup: function (t, cb) {
cb(null, YourConnectionHandler)
},
teardown: function (t, cb) {
cb()
}
}
#### JS

tests(tape, common)
```js
describe('your connection', () => {
require('interface-connection/src/tests')({
async setup () {
return YourConnection
},
async teardown () {
// cleanup resources created by setup()
}
})
})
```

## Go
#### Go

> WIP

# API
## API

A valid (read: that follows this abstraction) connection, must implement the following API.
### Connection

**Table of contents:**
A valid connection (one that follows this abstraction), must implement the following API:

- type: `Connection`
- `conn.getObservedAddrs(callback)`
- `conn.getPeerInfo(callback)`
- `conn.setPeerInfo(peerInfo)`
- `...`
```js
new Connection({
localAddr,
remoteAddr,
localPeer,
remotePeer,
newStream,
close,
getStreams,
stat: {
direction,
timeline: {
open,
upgraded
},
multiplexer,
encryption
}
})
```
- `<Multiaddr> conn.localAddr`
- `<Multiaddr> conn.remoteAddr`
- `<PeerId> conn.localPeer`
- `<PeerId> conn.remotePeer`
- `<Object> conn.stat`
- `<Map> conn.registry`
- `Array<Stream> conn.streams`
- `Promise<object> conn.newStream(Array<protocols>)`
vasco-santos marked this conversation as resolved.
Show resolved Hide resolved
- `<void> conn.removeStream(id)`
- `<Stream> conn.addStream(stream, protocol, metadata)`
- `Promise<> conn.close()`
vasco-santos marked this conversation as resolved.
Show resolved Hide resolved
vasco-santos marked this conversation as resolved.
Show resolved Hide resolved

It can be obtained as follows:

```js
const { Connection } = require('interface-connection')

const conn = new Connection({
localAddr: maConn.localAddr,
remoteAddr: maConn.remoteAddr,
localPeer: this._peerId,
remotePeer,
newStream,
close: err => maConn.close(err),
getStreams,
stats: {
direction: 'outbound',
timeline: {
open: maConn.timeline.open,
upgraded: Date.now()
},
multiplexer,
encryption
}
})
```

#### Creating a connection instance

- `JavaScript` - `const conn = new Connection({localAddr, remoteAddr, localPeer, remotePeer, newStream, close, getStreams, direction, multiplexer, encryption})`

vasco-santos marked this conversation as resolved.
Show resolved Hide resolved
Creates a new Connection instance.

`localAddr` is the [multiaddr](https://github.com/multiformats/multiaddr) address used by the local peer to reach the remote.
`remoteAddr` is the [multiaddr](https://github.com/multiformats/multiaddr) address used to communicate with the remote peer.
`localPeer` is the [PeerId](https://github.com/libp2p/js-peer-id) of the local peer.
`remotePeer` is the [PeerId](https://github.com/libp2p/js-peer-id) of the remote peer.
`newStream` is the `function` responsible for getting a new muxed+multistream-selected stream.
`close` is the `function` responsible for closing the raw connection.
`getStreams` is the `function` responsible for getting the streams muxed within the connection.
`stats` is an `object` with the metadata of the connection. It contains:
- `direction` is a `string` indicating whether the connection is `inbound` or `outbound`.
- `timeline` is an `object` with the relevant events timestamps of the connection (`open`, `upgraded` and `closed`; the `closed` will be added when the connection is closed).
- `multiplexer` is a `string` with the connection multiplexing codec (optional).
- `encryption` is a `string` with the connection encryption method identifier (optional).

#### Create a new stream

- `JavaScript` - `conn.newStream(protocols)`

Create a new stream within the connection.

`protocols` is an array of the intended protocol to use (by order of preference). Example: `[/echo/1.0.0]`

It returns a `Promise` with an object with the following properties:

```js
{
stream,
protocol
}
```

The stream property contains the muxed stream, while the protocol contains the protocol codec used by the stream.

#### Add stream metadata

- `JavaScript` - `conn.addStream(stream, { protocol, ...metadata })`

Add a new stream to the connection registry.

`stream` is a muxed stream.
`protocol` is the string codec for the protocol used by the stream. Example: `/echo/1.0.0`
`metadata` is an object containing any additional, optional, stream metadata that you wish to track (such as its `tags`).

#### Remove a from the registry

- `JavaScript` - `conn.removeStream(id)`

Removes the stream with the given id from the connection registry.

`id` is the unique id of the stream for this connection.


#### Close connection

- `JavaScript` - `conn.close()`

This method closes the connection to the remote peer, as well as all the streams muxed within the connection.

It returns a `Promise`.

#### Connection identifier

- `JavaScript` - `conn.id`

This property contains the identifier of the connection.

#### Connection streams registry

- `JavaScript` - `conn.registry`

This property contains a map with the muxed streams indexed by their id. This registry contains the protocol used by the stream, as well as its metadata.

#### Remote peer

- `JavaScript` - `conn.remotePeer`

This property contains the remote `peer-id` of this connection.

#### Local peer

- `JavaScript` - `conn.localPeer`

This property contains the local `peer-id` of this connection.

#### Get the connection Streams

- `JavaScript` - `conn.streams`

This getter returns all the muxed streams within the connection.

It returns an `Array`.

#### Remote address

- `JavaScript` - `conn.remoteAddr`

This getter returns the `remote` [multiaddr](https://github.com/multiformats/multiaddr) address.

#### Local address

- `JavaScript` - `conn.localAddr`

This getter returns the `local` [multiaddr](https://github.com/multiformats/multiaddr) address.

#### Stat

- `JavaScript` - `conn.stat`

This getter returns an `Object` with the metadata of the connection, as follows:

### Get the Observed Addresses of the peer in the other end
- `status`:

- `JavaScript` - `conn.getObservedAddrs(callback)`
This property contains the status of the connection. It can be either `open`, `closing` or `closed`. Once the connection is created it is in an `open` status. When a `conn.close()` happens, the status will change to `closing` and finally, after all the connection streams are properly closed, the status will be `closed`.

This method retrieves the observed addresses we get from the underlying transport, if any.
- `timeline`:

`callback` should follow the follow `function (err, multiaddrs) {}`, where `multiaddrs` is an array of [multiaddr](https://github.com/multiformats/multiaddr).
This property contains an object with the `open`, `upgraded` and `close` timestamps of the connection. Note that, the `close` timestamp is `undefined` until the connection is closed.

### Get the PeerInfo
- `direction`:

- `JavaScript` - `conn.getPeerInfo(callback)`
This property contains the direction of the peer in the connection. It can be `inbound` or `outbound`.

This method retrieves the a Peer Info object that contains information about the peer that this conn connects to.
- `multiplexer`:

`callback` should follow the `function (err, peerInfo) {}` signature, where peerInfo is a object of type [Peer Info](https://github.com/libp2p/js-peer-info)
This property contains the `multiplexing` codec being used in the connection.

### Set the PeerInfo
- `encryption`:

- `JavaScript` - `conn.setPeerInfo(peerInfo)`
j
This method stores a reference to the peerInfo Object that contains information about the peer that this conn connects to.
This property contains the encryption method being used in the connection. It is `undefined` if the connection is not encrypted.

`peerInfo` is a object of type [Peer Info](https://github.com/diasdavid/js-peer-info)
#### Tags

---
- `JavaScript` - `conn.tags`

notes:
- should follow the remaining Duplex stream operations
- should have backpressure into account
- should enable half duplex streams (close from one side, but still open for the other)
- should support full duplex
- tests should be performed by passing two streams
This property contains an array of tags associated with the connection. New tags can be pushed to this array during the connection's lifetime.
Loading