Compare commits

...

44 Commits

Author SHA1 Message Date
0d1c454ce0 chore: add coverage scripts
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2019-08-19 14:32:07 +02:00
3b162207b0 chore: update dependencies
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2019-08-19 14:29:45 +02:00
1c6500141b chore: log errors emited by server handler socket 2019-08-19 13:46:36 +02:00
fb50de5cc6 fix: throw error on write to destroyed socket 2019-08-19 13:46:36 +02:00
4a86c353e0 chore: update interface-transport 2019-08-19 13:46:36 +02:00
a96c612071 test: fix listen addresses in tests 2019-08-19 13:46:35 +02:00
1c1dfa65d3 chore: update packages 2019-08-19 13:46:35 +02:00
1a2fd636eb chore: nicer addr name in README 2019-08-19 13:46:35 +02:00
fb206cfd66 chore: fix travis file 2019-08-19 13:46:35 +02:00
ff9123d53c revert: "feat: listen to array of multiaddrs (#104)"
This reverts commit 5009c2ca49f5e3f1ce8e8fe5f0bd88edbc13b5d8.
2019-08-19 13:46:21 +02:00
c60897fbd8 chore: fix package.json 2019-08-19 13:46:02 +02:00
bba2084476 feat: abort after connect 2019-08-19 13:46:02 +02:00
d5be5ba7ed feat: listen to array of multiaddrs (#104)
* feat: support listen on array

* chore: fix missing deps

* chore: update interface version

* docs: update readme for array listen

* test: use port 0

* docs: add some more jsdocs

* chore: fix travis support for ip6 on linux

* refactor: clean up some code
2019-08-19 13:46:01 +02:00
b977a59fb8 chore: update README 2019-08-19 13:45:26 +02:00
5c8c6afea6 test: add async/await tests 2019-08-19 13:45:26 +02:00
e18a3d51a6 feat: cancel dials with AbortController 2019-08-19 13:43:56 +02:00
bc1aeefd0d chore: callbacks -> async / await
BREAKING CHANGE: All places in the API that used callbacks are now replaced with async/await
2019-08-19 13:43:32 +02:00
49c7f33375 chore: release version v0.13.1
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2019-08-08 13:27:01 +02:00
89bceb461f chore: update contributors 2019-08-08 13:27:01 +02:00
44f9e32d07 chore: update deps (#110)
* chore: add node 12 to travis
* test: just use spec files
2019-08-08 12:48:13 +02:00
c26cc70c65 chore: add discourse badge (#106) 2019-04-26 17:44:28 +02:00
3ab43a3604 chore: prefer const over let (#99)
Prefer const over let when the binding is static, in order to comply with an upcoming Standard rule.
2018-10-31 08:35:55 +00:00
3aad2ed243 chore: release version v0.13.0
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2018-09-12 19:40:49 +02:00
01cfbda2e7 chore: update contributors 2018-09-12 19:40:49 +02:00
eba0b48744 feat: add support for dialing over dns
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2018-09-12 19:38:02 +02:00
a0c23e49f7 chore: release version v0.12.1
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2018-07-31 14:13:11 +02:00
66ab208182 chore: update contributors 2018-07-31 14:13:10 +02:00
168d111158 chore: update deps and fix test runner
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2018-07-31 14:08:31 +02:00
4b04b17dfa fix: invalid ip address and daemon can be crashed by remote user
Per the nodeJS documentation, a Net socket.remoteAddress value may
be undefined if the socket is destroyed, as by a client disconnect.
A multiaddr cannot be created for an invalid IP address (such as
the undefined remote address of a destroyed socket). Currently
the attempt results in a crash that can be triggered remotely. This
commit catches the exception in get-multiaddr and returns an
undefined value to listener rather than throwing an exception when
trying to process defective or destroyed socket data. Listener then
terminates processing of the incoming p2p connections that generate
this error condition.

fixes: https://github.com/libp2p/js-libp2p-tcp/issues/93
fixes: https://github.com/ipfs/js-ipfs/issues/1447
2018-07-31 13:51:27 +02:00
6c36a46831 test: fixes listen-dial test "dial and destroy on listener" (#97) 2018-07-31 13:46:12 +02:00
d39ec2db40 chore: add lead maintainer (#94)
* chore: add lead maintainer

License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>

* Update package.json
2018-06-26 17:58:30 +02:00
79428f3e62 chore: release version v0.12.0 2018-04-05 17:00:33 +01:00
8a394b5286 chore: update contributors 2018-04-05 17:00:33 +01:00
b7f73bcda1 test: fix dial error test 2018-04-05 17:00:19 +01:00
8b44aa28ee chore: update deps 2018-04-05 16:56:23 +01:00
ded1f6831c feat: add class-is module 2018-04-05 16:55:54 +01:00
5ef24695fc docs: fixing the broken example in README (#91)
* Working example fix

* Char fix

* Corrected output, and add yarn to gitignore
2018-04-05 16:55:25 +01:00
df0aa059ca chore: release version v0.11.6 2018-02-20 10:27:27 +00:00
bf74e9acad chore: update contributors 2018-02-20 10:27:26 +00:00
83c4617e8d chore: update deps 2018-02-20 10:27:17 +00:00
3b7e68bc8a docs: update and polish 2018-02-20 10:25:18 +00:00
de1d7fe75c chore: tiny refactor 2018-02-07 06:40:00 +00:00
9c77a69ae3 chore: tiny refactor 2018-02-07 06:26:02 +00:00
50840a8067 test: refactor 2018-02-07 06:06:15 +00:00
22 changed files with 865 additions and 351 deletions

4
.gitignore vendored
View File

@ -4,11 +4,15 @@ docs
test/repo-tests*
**/bundle.js
# yarn
yarn.lock
# Logs
logs
*.log
coverage
.nyc_output
# Runtime data
pids

View File

@ -1,32 +1,34 @@
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
sudo: false
language: node_js
cache: npm
sudo: false
matrix:
include:
- node_js: 6
env: CXX=g++-4.8
- node_js: 8
env: CXX=g++-4.8
# - node_js: stable
# env: CXX=g++-4.8
stages:
- check
- test
- cov
script:
- npm run lint
- npm run test
- npm run coverage
node_js:
- '10'
- '12'
os:
- linux
- osx
- windows
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; fi
script: npx nyc -s npm run test:node -- --bail
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
after_success:
- npm run coverage-publish
jobs:
include:
- stage: check
os: linux
script:
- npx aegir build --bundlesize
- npx aegir dep-check
- npm run lint
addons:
firefox: 'latest'
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
notifications:
email: false

View File

@ -1,3 +1,43 @@
<a name="0.13.1"></a>
## [0.13.1](https://github.com/libp2p/js-libp2p-tcp/compare/v0.13.0...v0.13.1) (2019-08-08)
<a name="0.13.0"></a>
# [0.13.0](https://github.com/libp2p/js-libp2p-tcp/compare/v0.12.1...v0.13.0) (2018-09-12)
### Features
* add support for dialing over dns ([eba0b48](https://github.com/libp2p/js-libp2p-tcp/commit/eba0b48))
<a name="0.12.1"></a>
## [0.12.1](https://github.com/libp2p/js-libp2p-tcp/compare/v0.12.0...v0.12.1) (2018-07-31)
### Bug Fixes
* invalid ip address and daemon can be crashed by remote user ([4b04b17](https://github.com/libp2p/js-libp2p-tcp/commit/4b04b17))
<a name="0.12.0"></a>
# [0.12.0](https://github.com/libp2p/js-libp2p-tcp/compare/v0.11.6...v0.12.0) (2018-04-05)
### Features
* add class-is module ([ded1f68](https://github.com/libp2p/js-libp2p-tcp/commit/ded1f68))
<a name="0.11.6"></a>
## [0.11.6](https://github.com/libp2p/js-libp2p-tcp/compare/v0.11.5...v0.11.6) (2018-02-20)
<a name="0.11.5"></a>
## [0.11.5](https://github.com/libp2p/js-libp2p-tcp/compare/v0.11.4...v0.11.5) (2018-02-07)

View File

@ -1,31 +1,29 @@
# js-libp2p-tcp
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![Build Status](https://travis-ci.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://travis-ci.org/libp2p/js-libp2p-tcp)
[![Coverage Status](https://coveralls.io/repos/github/libp2p/js-libp2p-tcp/badge.svg?branch=master)](https://coveralls.io/github/libp2p/js-libp2p-tcp?branch=master)
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai)
[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
[![](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-tcp)
[![](https://img.shields.io/travis/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://travis-ci.com/libp2p/js-libp2p-tcp)
[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-tcp)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square)
![](https://img.shields.io/badge/Node.js-%3E%3D6.0.0-orange.svg?style=flat-square)
![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png)
![](https://raw.githubusercontent.com/libp2p/interface-transport/master/img/badge.png)
[![](https://raw.githubusercontent.com/libp2p/interface-transport/master/img/badge.png)](https://github.com/libp2p/interface-transport)
[![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png)](https://github.com/libp2p/interface-connection)
> Node.js implementation of the TCP module that libp2p uses, which implements the [interface-connection](https://github.com/libp2p/interface-connection) interface for dial/listen.
`libp2p-tcp` in Node.js is a very thin shim that adds support for dialing to a `multiaddr`. This small shim will enable libp2p to use other different transports.
> JavaScript implementation of the TCP module for libp2p. It exposes the [interface-transport](https://github.com/libp2p/interface-connection) for dial/listen. `libp2p-tcp` is a very thin shim that adds support for dialing to a `multiaddr`. This small shim will enable libp2p to use other transports.
**Note:** This module uses [pull-streams](https://pull-stream.github.io) for all stream based interfaces.
## Lead Maintainer
[Jacob Heun](https://github.com/jacobheun)
## Table of Contents
- [Install](#install)
- [npm](#npm)
- [Usage](#usage)
- [Example](#example)
- [This module uses `pull-streams`](#this-module-uses-pull-streams)
- [Converting `pull-streams` to Node.js Streams](#converting-pull-streams-to-nodejs-streams)
- [API](#api)
- [Contribute](#contribute)
- [License](#license)
@ -35,42 +33,41 @@
### npm
```sh
> npm i libp2p-tcp
> npm install libp2p-tcp
```
## Usage
### Example
```js
const TCP = require('libp2p-tcp')
const multiaddr = require('multiaddr')
const pull = require('pull-stream')
const pipe = require('it-pipe')
const { collect } = require('streaming-iterables')
const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090')
const mh2 = multiaddr('/ip6/::/tcp/9092')
const addr = multiaddr('/ip4/127.0.0.1/tcp/9090')
const tcp = new TCP()
const listener = tcp.createListener(mh1, (socket) => {
const listener = tcp.createListener((socket) => {
console.log('new connection opened')
pull(
pull.values(['hello']),
pipe(
['hello'],
socket
)
})
listener.listen(() => {
console.log('listening')
await listener.listen(addr)
console.log('listening')
pull(
tcp.dial(mh1),
pull.log,
pull.onEnd(() => {
tcp.close()
})
)
})
const socket = await tcp.dial(addr)
const values = await pipe(
socket,
collect
)
console.log(`Value: ${values.toString()}`)
// Close connection after reading
await listener.close()
```
Outputs:
@ -78,43 +75,25 @@ Outputs:
```sh
listening
new connection opened
hello
Value: hello
```
### This module uses `pull-streams`
We expose a streaming interface based on `pull-streams`, rather then on the Node.js core streams implementation (aka Node.js streams). `pull-streams` offers us a better mechanism for error handling and flow control guarantees. If you would like to know more about why we did this, see the discussion at this [issue](https://github.com/ipfs/js-ipfs/issues/362).
You can learn more about pull-streams at:
- [The history of Node.js streams, nodebp April 2014](https://www.youtube.com/watch?v=g5ewQEuXjsQ)
- [The history of streams, 2016](http://dominictarr.com/post/145135293917/history-of-streams)
- [pull-streams, the simple streaming primitive](http://dominictarr.com/post/149248845122/pull-streams-pull-streams-are-a-very-simple)
- [pull-streams documentation](https://pull-stream.github.io/)
#### Converting `pull-streams` to Node.js Streams
If you are a Node.js streams user, you can convert a pull-stream to a Node.js stream using the module [`pull-stream-to-stream`](https://github.com/dominictarr/pull-stream-to-stream), giving you an instance of a Node.js stream that is linked to the pull-stream. For example:
```js
const pullToStream = require('pull-stream-to-stream')
const nodeStreamInstance = pullToStream(pullStreamInstance)
// nodeStreamInstance is an instance of a Node.js Stream
```
To learn more about this utility, visit https://pull-stream.github.io/#pull-stream-to-stream.
## API
### Transport
[![](https://raw.githubusercontent.com/libp2p/interface-transport/master/img/badge.png)](https://github.com/libp2p/interface-transport)
`libp2p-tcp` accepts TCP addresses both IPFS and non IPFS encapsulated addresses, i.e:
`libp2p-tcp` accepts TCP addresses as both IPFS and non IPFS encapsulated addresses, i.e:
`/ip4/127.0.0.1/tcp/4001`
`/ip4/127.0.0.1/tcp/4001/ipfs/QmHash`
Both for dialing and listening.
(both for dialing and listening)
### Connection
[![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png)](https://github.com/libp2p/interface-connection)
## Contribute

View File

@ -1,29 +0,0 @@
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
version: "{build}"
environment:
matrix:
- nodejs_version: "6"
- nodejs_version: "8"
matrix:
fast_finish: true
install:
# Install Node.js
- ps: Install-Product node $env:nodejs_version
# Upgrade npm
- npm install -g npm
# Output our current versions for debugging
- node --version
- npm --version
# Install our package dependencies
- npm install
test_script:
- npm run test:node
build: off

2
ci/Jenkinsfile vendored
View File

@ -1,2 +0,0 @@
// Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
javascript()

View File

@ -1,15 +0,0 @@
# Warning: This file is automatically synced from https://github.com/ipfs/ci-sync so if you want to change it, please change it there and ask someone to sync all repositories.
machine:
node:
version: stable
dependencies:
pre:
- google-chrome --version
- curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- sudo dpkg -i google-chrome.deb || true
- sudo apt-get update
- sudo apt-get install -f
- sudo apt-get install --only-upgrade lsb-base
- sudo dpkg -i google-chrome.deb
- google-chrome --version

View File

@ -1,20 +1,22 @@
{
"name": "libp2p-tcp",
"version": "0.11.5",
"version": "0.13.1",
"description": "Node.js implementation of the TCP module that libp2p uses, which implements the interface-connection and interface-transport interfaces",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js",
"scripts": {
"lint": "aegir lint",
"test": "aegir test -t node",
"test:node": "aegir test -t node",
"build": "aegir build",
"docs": "aegir docs",
"release": "aegir release -t node --no-build",
"release-minor": "aegir release -t node --type minor --no-build",
"release-major": "aegir-release -t node --type major --no-build",
"coverage": "aegir coverage",
"coverage-publish": "aegir coverage --provider coveralls"
"coverage": "nyc --reporter=text --reporter=lcov npm run test:node"
},
"pre-push": [
"lint",
"test"
"lint"
],
"repository": {
"type": "git",
@ -23,7 +25,6 @@
"keywords": [
"IPFS"
],
"author": "David Dias <daviddias@ipfs.io>",
"license": "MIT",
"bugs": {
"url": "https://github.com/libp2p/js-libp2p-tcp/issues"
@ -34,36 +35,45 @@
"npm": ">=3.0.0"
},
"devDependencies": {
"aegir": "^12.4.0",
"chai": "^4.1.2",
"aegir": "^20.0.0",
"chai": "^4.2.0",
"dirty-chai": "^2.0.1",
"interface-transport": "~0.3.5",
"lodash.isfunction": "^3.0.9",
"pre-commit": "^1.2.2",
"pull-stream": "^3.6.1"
"pull-stream": "^3.6.9",
"sinon": "^7.4.1"
},
"dependencies": {
"debug": "^3.1.0",
"interface-connection": "~0.3.2",
"ip-address": "^5.8.9",
"abortable-iterator": "^2.1.0",
"class-is": "^1.1.0",
"debug": "^4.1.1",
"err-code": "^1.1.2",
"interface-connection": "~0.3.3",
"interface-transport": "~0.5.2",
"ip-address": "^6.1.0",
"it-pipe": "^1.0.1",
"lodash.includes": "^4.3.0",
"lodash.isfunction": "^3.0.9",
"mafmt": "^3.0.2",
"multiaddr": "^3.0.2",
"once": "^1.4.0",
"stream-to-pull-stream": "^1.7.2"
"mafmt": "^6.0.8",
"multiaddr": "^6.1.0",
"streaming-iterables": "^4.1.0"
},
"contributors": [
"Alan Shaw <alan@tableflip.io>",
"David Dias <daviddias.p@gmail.com>",
"Diogo Silva <fsdiogo@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"Drew Stone <drewstone329@gmail.com>",
"Evan Schwartz <evan.mark.schwartz@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Greenkeeper <support@greenkeeper.io>",
"Jacob Heun <jacobheun@gmail.com>",
"Jacob Heun <jake@andyet.net>",
"João Antunes <j.goncalo.antunes@gmail.com>",
"Linus Unnebäck <linus@folkdatorn.se>",
"Pedro Teixeira <i@pgte.me>",
"Prashanth Chandra <coolshanth94@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>",
"Stephen Whitmore <stephen.whitmore@gmail.com>"
"Stephen Whitmore <stephen.whitmore@gmail.com>",
"TomCoded <tomtinkerer@gmail.com>",
"Vasco Santos <vasco.santos@moxy.studio>"
]
}

17
src/adapter.js Normal file
View File

@ -0,0 +1,17 @@
'use strict'
const { Adapter } = require('interface-transport')
const withIs = require('class-is')
const TCP = require('.')
// Legacy adapter to old transport & connection interface
class TcpAdapter extends Adapter {
constructor () {
super(new TCP())
}
}
module.exports = withIs(TcpAdapter, {
className: 'TCP',
symbolName: '@libp2p/js-libp2p-tcp/tcp'
})

8
src/constants.js Normal file
View File

@ -0,0 +1,8 @@
'use strict'
// IPFS multi-address code
module.exports.IPFS_MA_CODE = 421
// Time to wait for a connection to close gracefully before destroying it
// manually
module.exports.CLOSE_TIMEOUT = 2000

View File

@ -2,21 +2,32 @@
const multiaddr = require('multiaddr')
const Address6 = require('ip-address').Address6
const debug = require('debug')
const log = debug('libp2p:tcp:get-multiaddr')
module.exports = (socket) => {
let mh
let ma
if (socket.remoteFamily === 'IPv6') {
var addr = new Address6(socket.remoteAddress)
if (addr.v4) {
var ip4 = addr.to4().correctForm()
mh = multiaddr('/ip4/' + ip4 + '/tcp/' + socket.remotePort)
try {
if (socket.remoteFamily === 'IPv6') {
const addr = new Address6(socket.remoteAddress)
if (addr.v4) {
const ip4 = addr.to4().correctForm()
ma = multiaddr('/ip4/' + ip4 +
'/tcp/' + socket.remotePort
)
} else {
ma = multiaddr('/ip6/' + socket.remoteAddress +
'/tcp/' + socket.remotePort
)
}
} else {
mh = multiaddr('/ip6/' + socket.remoteAddress + '/tcp/' + socket.remotePort)
ma = multiaddr('/ip4/' + socket.remoteAddress +
'/tcp/' + socket.remotePort)
}
} else {
mh = multiaddr('/ip4/' + socket.remoteAddress + '/tcp/' + socket.remotePort)
} catch (err) {
log(err)
}
return mh
return ma
}

View File

@ -1,55 +1,76 @@
'use strict'
const net = require('net')
const toPull = require('stream-to-pull-stream')
const mafmt = require('mafmt')
const withIs = require('class-is')
const includes = require('lodash.includes')
const isFunction = require('lodash.isfunction')
const Connection = require('interface-connection').Connection
const once = require('once')
const errcode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:tcp:dial')
const Libp2pSocket = require('./socket')
const createListener = require('./listener')
const { AbortError } = require('interface-transport')
function noop () {}
class TCP {
dial (ma, options, callback) {
if (isFunction(options)) {
callback = options
options = {}
}
callback = callback || noop
callback = once(callback)
async dial (ma, options) {
const cOpts = ma.toOptions()
log('Connecting to %s %s', cOpts.port, cOpts.host)
log('Dialing %s:%s', cOpts.host, cOpts.port)
const rawSocket = net.connect(cOpts)
const rawSocket = await this._connect(cOpts, options)
return new Libp2pSocket(rawSocket, ma, options)
}
rawSocket.once('timeout', () => {
log('timeout')
rawSocket.emit('error', new Error('Timeout'))
_connect (cOpts, options = {}) {
return new Promise((resolve, reject) => {
if ((options.signal || {}).aborted) {
return reject(new AbortError())
}
const start = Date.now()
const rawSocket = net.connect(cOpts)
const onError = (err) => {
const msg = `Error dialing ${cOpts.host}:${cOpts.port}: ${err.message}`
done(errcode(msg, err.code))
}
const onTimeout = () => {
log('Timeout dialing %s:%s', cOpts.host, cOpts.port)
const err = errcode(`Timeout after ${Date.now() - start}ms`, 'ETIMEDOUT')
// Note: this will result in onError() being called
rawSocket.emit('error', err)
}
const onConnect = () => {
log('Connected to %s:%s', cOpts.host, cOpts.port)
done(null, rawSocket)
}
const onAbort = () => {
log('Dial to %s:%s aborted', cOpts.host, cOpts.port)
rawSocket.destroy()
done(new AbortError())
}
const done = (err, res) => {
rawSocket.removeListener('error', onError)
rawSocket.removeListener('timeout', onTimeout)
rawSocket.removeListener('connect', onConnect)
options.signal && options.signal.removeEventListener('abort', onAbort)
err ? reject(err) : resolve(res)
}
rawSocket.once('error', onError)
rawSocket.once('timeout', onTimeout)
rawSocket.once('connect', onConnect)
options.signal && options.signal.addEventListener('abort', onAbort)
})
rawSocket.once('error', callback)
rawSocket.once('connect', () => {
rawSocket.removeListener('error', callback)
callback()
})
const socket = toPull.duplex(rawSocket)
const conn = new Connection(socket)
conn.getObservedAddrs = (callback) => {
return callback(null, [ma])
}
return conn
}
createListener (options, handler) {
@ -58,8 +79,7 @@ class TCP {
options = {}
}
handler = handler || (() => {})
handler = handler || noop
return createListener(handler)
}
@ -82,4 +102,4 @@ class TCP {
}
}
module.exports = TCP
module.exports = withIs(TCP, { className: 'TCP', symbolName: '@libp2p/js-libp2p-tcp/tcp' })

View File

@ -1,43 +1,44 @@
'use strict'
const multiaddr = require('multiaddr')
const Connection = require('interface-connection').Connection
const os = require('os')
const includes = require('lodash.includes')
const net = require('net')
const toPull = require('stream-to-pull-stream')
const EventEmitter = require('events').EventEmitter
const debug = require('debug')
const log = debug('libp2p:tcp:listen')
const logError = debug('libp2p:tcp:listen:error')
const Libp2pSocket = require('./socket')
const getMultiaddr = require('./get-multiaddr')
const IPFS_CODE = 421
const CLOSE_TIMEOUT = 2000
function noop () {}
const c = require('./constants')
module.exports = (handler) => {
const listener = new EventEmitter()
const server = net.createServer((socket) => {
// Avoid uncaught errors cause by unstable connections
socket.on('error', noop)
// Avoid uncaught errors caused by unstable connections
socket.on('error', (err) => {
logError('Error emitted by server handler socket: ' + err.message)
})
const addr = getMultiaddr(socket)
log('new connection', addr.toString())
const s = toPull.duplex(socket)
s.getObservedAddrs = (cb) => {
cb(null, [addr])
if (!addr) {
if (socket.remoteAddress === undefined) {
log('connection closed before p2p connection made')
} else {
log('error interpreting incoming p2p connection')
}
return
}
log('new connection', addr.toString())
const s = new Libp2pSocket(socket, addr)
trackSocket(server, socket)
const conn = new Connection(s)
handler(conn)
listener.emit('connection', conn)
handler && handler(s)
listener.emit('connection', s)
})
server.on('listening', () => listener.emit('listening'))
@ -47,33 +48,35 @@ module.exports = (handler) => {
// Keep track of open connections to destroy in case of timeout
server.__connections = {}
listener.close = (options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
listener.close = (options = {}) => {
if (!server.listening) {
return
}
callback = callback || noop
options = options || {}
const timeout = setTimeout(() => {
log('unable to close graciously, destroying conns')
Object.keys(server.__connections).forEach((key) => {
log('destroying %s', key)
server.__connections[key].destroy()
})
}, options.timeout || CLOSE_TIMEOUT)
return new Promise((resolve, reject) => {
const start = Date.now()
server.close(callback)
// Attempt to stop the server. If it takes longer than the timeout,
// destroy all the underlying sockets manually.
const timeout = setTimeout(() => {
log('Timeout closing server after %dms, destroying connections manually', Date.now() - start)
Object.keys(server.__connections).forEach((key) => {
log('destroying %s', key)
server.__connections[key].destroy()
})
resolve()
}, options.timeout || c.CLOSE_TIMEOUT)
server.once('close', () => {
clearTimeout(timeout)
server.once('close', () => clearTimeout(timeout))
server.close((err) => err ? reject(err) : resolve())
})
}
let ipfsId
let listeningAddr
listener.listen = (ma, callback) => {
listener.listen = (ma) => {
listeningAddr = ma
if (includes(ma.protoNames(), 'ipfs')) {
ipfsId = getIpfsId(ma)
@ -81,16 +84,24 @@ module.exports = (handler) => {
}
const lOpts = listeningAddr.toOptions()
log('Listening on %s %s', lOpts.port, lOpts.host)
return server.listen(lOpts.port, lOpts.host, callback)
return new Promise((resolve, reject) => {
server.listen(lOpts.port, lOpts.host, (err) => {
if (err) {
return reject(err)
}
log('Listening on %s %s', lOpts.port, lOpts.host)
resolve()
})
})
}
listener.getAddrs = (callback) => {
listener.getAddrs = () => {
const multiaddrs = []
const address = server.address()
if (!address) {
return callback(new Error('Listener is not ready yet'))
throw new Error('Listener is not ready yet')
}
// Because TCP will only return the IPv6 version
@ -125,7 +136,7 @@ module.exports = (handler) => {
multiaddrs.push(ma)
}
callback(null, multiaddrs)
return multiaddrs
}
return listener
@ -133,7 +144,7 @@ module.exports = (handler) => {
function getIpfsId (ma) {
return ma.stringTuples().filter((tuple) => {
return tuple[0] === IPFS_CODE
return tuple[0] === c.IPFS_MA_CODE
})[0][1]
}
@ -141,7 +152,7 @@ function trackSocket (server, socket) {
const key = `${socket.remoteAddress}:${socket.remotePort}`
server.__connections[key] = socket
socket.on('close', () => {
socket.once('close', () => {
delete server.__connections[key]
})
}

85
src/socket.js Normal file
View File

@ -0,0 +1,85 @@
'use strict'
const abortable = require('abortable-iterator')
const debug = require('debug')
const log = debug('libp2p:tcp:socket')
const c = require('./constants')
class Libp2pSocket {
constructor (rawSocket, ma, opts = {}) {
this._rawSocket = rawSocket
this._ma = ma
this.sink = this._sink(opts)
this.source = opts.signal ? abortable(rawSocket, opts.signal) : rawSocket
}
_sink (opts) {
// By default, close when the source is exhausted
const closeOnEnd = opts.closeOnEnd !== false
return async (source) => {
try {
const src = opts.signal ? abortable(source, opts.signal) : source
await this._write(src, closeOnEnd)
} catch (err) {
// If the connection is aborted just close the socket
if (err.type === 'aborted') {
return this.close()
}
throw err
}
}
}
async _write (source, closeOnEnd) {
for await (const data of source) {
if (this._rawSocket.destroyed) {
const cOpts = this._ma.toOptions()
throw new Error('Cannot write %d bytes to destroyed socket %s:%s',
data.length, cOpts.host, cOpts.port)
}
const flushed = this._rawSocket.write(data)
if (!flushed) {
await new Promise((resolve) => this._rawSocket.once('drain', resolve))
}
}
if (closeOnEnd) {
await this.close()
}
}
close (opts = {}) {
if (this._rawSocket.pending || this._rawSocket.destroyed) {
return
}
return new Promise((resolve, reject) => {
const start = Date.now()
// Attempt to end the socket. If it takes longer to close than the
// timeout, destroy it manually.
const timeout = setTimeout(() => {
const cOpts = this._ma.toOptions()
log('Timeout closing socket to %s:%s after %dms, destroying it manually',
cOpts.host, cOpts.port, Date.now() - start)
this._rawSocket.destroy()
resolve()
}, opts.timeout || c.CLOSE_TIMEOUT)
this._rawSocket.once('close', () => clearTimeout(timeout))
this._rawSocket.end((err) => err ? reject(err) : resolve())
})
}
getObservedAddrs () {
return [this._ma]
}
}
module.exports = Libp2pSocket

View File

@ -6,7 +6,7 @@ const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const TCP = require('../src')
const TCP = require('../../src/adapter')
const multiaddr = require('multiaddr')
const Connection = require('interface-connection').Connection
@ -89,7 +89,7 @@ describe('Connection Wrap', () => {
})
it('dial error', (done) => {
tcp.dial(multiaddr('/ip4/999.0.0.1/tcp/1234'), (err) => {
tcp.dial(multiaddr('/ip4/127.0.0.1/tcp/22234'), (err) => {
expect(err).to.exist()
done()
})

View File

@ -0,0 +1,111 @@
/* eslint-env mocha */
'use strict'
const pull = require('pull-stream')
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const TCP = require('../../src/adapter')
const multiaddr = require('multiaddr')
describe('valid Connection', () => {
let tcp
beforeEach(() => {
tcp = new TCP()
})
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090')
it('get observed addrs', (done) => {
let dialerObsAddrs
const listener = tcp.createListener((conn) => {
expect(conn).to.exist()
conn.getObservedAddrs((err, addrs) => {
expect(err).to.not.exist()
dialerObsAddrs = addrs
pull(pull.empty(), conn)
})
})
listener.listen(ma, () => {
const conn = tcp.dial(ma)
pull(
conn,
pull.onEnd(endHandler)
)
function endHandler () {
conn.getObservedAddrs((err, addrs) => {
expect(err).to.not.exist()
pull(pull.empty(), conn)
closeAndAssert(listener, addrs)
})
}
function closeAndAssert (listener, addrs) {
listener.close(() => {
expect(addrs[0]).to.deep.equal(ma)
expect(dialerObsAddrs.length).to.equal(1)
done()
})
}
})
})
it('get Peer Info', (done) => {
const listener = tcp.createListener((conn) => {
expect(conn).to.exist()
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.exist()
expect(peerInfo).to.not.exist()
pull(pull.empty(), conn)
})
})
listener.listen(ma, () => {
const conn = tcp.dial(ma)
pull(conn, pull.onEnd(endHandler))
function endHandler () {
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.exist()
expect(peerInfo).to.not.exist()
listener.close(done)
})
}
})
})
it('set Peer Info', (done) => {
const listener = tcp.createListener((conn) => {
expect(conn).to.exist()
conn.setPeerInfo('batatas')
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.not.exist()
expect(peerInfo).to.equal('batatas')
pull(pull.empty(), conn)
})
})
listener.listen(ma, () => {
const conn = tcp.dial(ma)
pull(conn, pull.onEnd(endHandler))
function endHandler () {
conn.setPeerInfo('arroz')
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.not.exist()
expect(peerInfo).to.equal('arroz')
listener.close(done)
})
}
})
})
})

View File

@ -6,7 +6,7 @@ const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const TCP = require('../src')
const TCP = require('../../src/adapter')
const net = require('net')
const multiaddr = require('multiaddr')
const isCI = process.env.CI
@ -194,10 +194,9 @@ describe('dial', () => {
})
})
// TODO: figure out why is this failing
it.skip('dial and destroy on listener', (done) => {
it('dial and destroy on listener', (done) => {
let count = 0
const closed = ++count === 2 ? finish() : null
const closed = () => ++count === 2 ? finish() : null
const ma = multiaddr('/ip6/::/tcp/9067')

View File

@ -1,23 +1,43 @@
/* eslint-env mocha */
'use strict'
const sinon = require('sinon')
const tests = require('interface-transport')
const multiaddr = require('multiaddr')
const Tcp = require('../src')
const net = require('net')
const TCP = require('../src')
describe('interface-transport compliance', () => {
tests({
setup (cb) {
let tcp = new Tcp()
setup () {
const tcp = new TCP()
const addrs = [
multiaddr('/ip4/127.0.0.1/tcp/9091'),
multiaddr('/ip4/127.0.0.1/tcp/9092'),
multiaddr('/ip4/127.0.0.1/tcp/9093')
multiaddr('/ip4/127.0.0.1/tcp/9093'),
multiaddr('/dns4/ipfs.io')
]
cb(null, tcp, addrs)
},
teardown (cb) {
cb()
// Used by the dial tests to simulate a delayed connect
const connector = {
delay (delayMs) {
const netConnect = net.connect
sinon.replace(net, 'connect', (opts) => {
const socket = netConnect(opts)
const socketEmit = socket.emit.bind(socket)
sinon.replace(socket, 'emit', (...args) => {
const time = args[0] === 'connect' ? delayMs : 0
setTimeout(() => socketEmit(...args), time)
})
return socket
})
},
restore () {
sinon.restore()
}
}
return { transport: tcp, addrs, connector }
}
})
})

View File

@ -1,7 +1,6 @@
/* eslint-env mocha */
'use strict'
const pull = require('pull-stream')
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
@ -18,94 +17,39 @@ describe('valid Connection', () => {
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090')
it('get observed addrs', (done) => {
let dialerObsAddrs
it('get observed addrs', async () => {
// Create a Promise that resolves when a connection is handled
let handled
const handlerPromise = new Promise((resolve) => {
handled = resolve
})
const listener = tcp.createListener((conn) => {
const handler = async (conn) => {
expect(conn).to.exist()
conn.getObservedAddrs((err, addrs) => {
expect(err).to.not.exist()
dialerObsAddrs = addrs
pull(pull.empty(), conn)
})
})
const dialerObsAddrs = await conn.getObservedAddrs()
handled(dialerObsAddrs)
}
listener.listen(ma, () => {
const conn = tcp.dial(ma)
pull(
conn,
pull.onEnd(endHandler)
)
// Create a listener with the handler
const listener = tcp.createListener(handler)
function endHandler () {
conn.getObservedAddrs((err, addrs) => {
expect(err).to.not.exist()
pull(pull.empty(), conn)
closeAndAssert(listener, addrs)
})
}
// Listen on the multi-address
await listener.listen(ma)
function closeAndAssert (listener, addrs) {
listener.close(() => {
expect(addrs[0]).to.deep.equal(ma)
expect(dialerObsAddrs.length).to.equal(1)
done()
})
}
})
})
// Dial to that same address
const conn = await tcp.dial(ma)
const addrs = await conn.getObservedAddrs()
it('get Peer Info', (done) => {
const listener = tcp.createListener((conn) => {
expect(conn).to.exist()
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.exist()
expect(peerInfo).to.not.exist()
pull(pull.empty(), conn)
})
})
// Wait for the incoming dial to be handled
const dialerObsAddrs = await handlerPromise
listener.listen(ma, () => {
const conn = tcp.dial(ma)
// Close the listener
await listener.close()
pull(conn, pull.onEnd(endHandler))
function endHandler () {
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.exist()
expect(peerInfo).to.not.exist()
listener.close(done)
})
}
})
})
it('set Peer Info', (done) => {
const listener = tcp.createListener((conn) => {
expect(conn).to.exist()
conn.setPeerInfo('batatas')
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.not.exist()
expect(peerInfo).to.equal('batatas')
pull(pull.empty(), conn)
})
})
listener.listen(ma, () => {
const conn = tcp.dial(ma)
pull(conn, pull.onEnd(endHandler))
function endHandler () {
conn.setPeerInfo('arroz')
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.not.exist()
expect(peerInfo).to.equal('arroz')
listener.close(done)
})
}
})
// The addresses should match
expect(addrs.length).to.equal(1)
expect(addrs[0]).to.deep.equal(ma)
expect(dialerObsAddrs.length).to.equal(1)
expect(dialerObsAddrs[0]).to.exist()
})
})

View File

@ -9,6 +9,9 @@ const TCP = require('../src')
const multiaddr = require('multiaddr')
describe('filter addrs', () => {
const base = '/ip4/127.0.0.1'
const ipfs = '/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw'
let tcp
before(() => {
@ -16,25 +19,26 @@ describe('filter addrs', () => {
})
it('filter valid addrs for this transport', () => {
const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090')
const mh2 = multiaddr('/ip4/127.0.0.1/udp/9090')
const mh3 = multiaddr('/ip4/127.0.0.1/tcp/9090/http')
const mh4 = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const mh5 = multiaddr('/ip4/127.0.0.1/tcp/9090/http/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const mh6 = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw' +
'/p2p-circuit/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const ma1 = multiaddr(base + '/tcp/9090')
const ma2 = multiaddr(base + '/udp/9090')
const ma3 = multiaddr(base + '/tcp/9090/http')
const ma4 = multiaddr(base + '/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const ma5 = multiaddr(base + '/tcp/9090/http' + ipfs)
const ma6 = multiaddr('/ip4/127.0.0.1/tcp/9090/p2p-circuit' + ipfs)
const ma7 = multiaddr('/dns4/libp2p.io/tcp/9090')
const ma8 = multiaddr('/dnsaddr/libp2p.io/tcp/9090')
const valid = tcp.filter([mh1, mh2, mh3, mh4, mh5, mh6])
expect(valid.length).to.equal(2)
expect(valid[0]).to.deep.equal(mh1)
expect(valid[1]).to.deep.equal(mh4)
const valid = tcp.filter([ma1, ma2, ma3, ma4, ma5, ma6, ma7, ma8])
expect(valid.length).to.equal(4)
expect(valid[0]).to.deep.equal(ma1)
expect(valid[1]).to.deep.equal(ma4)
})
it('filter a single addr for this transport', () => {
const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090')
const ma1 = multiaddr(base + '/tcp/9090')
const valid = tcp.filter(mh1)
const valid = tcp.filter(ma1)
expect(valid.length).to.equal(1)
expect(valid[0]).to.deep.equal(mh1)
expect(valid[0]).to.eql(ma1)
})
})

View File

@ -0,0 +1,54 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const getMultiaddr = require('../src/get-multiaddr')
const goodSocket4 = {
remoteAddress: '127.0.0.1',
remotePort: '9090',
remoteFamily: 'IPv4'
}
const goodSocket6 = {
remoteAddress: '::1',
remotePort: '9090',
remoteFamily: 'IPv6'
}
const badSocket = {}
const badSocketData = {
remoteAddress: 'aewmrn4awoew',
remotePort: '234',
remoteFamily: 'Hufflepuff'
}
describe('getMultiaddr multiaddr creation', () => {
it('creates multiaddr from valid socket data', (done) => {
expect(getMultiaddr(goodSocket4))
.to.exist()
done()
})
it('creates multiaddr from valid IPv6 socket data', (done) => {
expect(getMultiaddr(goodSocket6))
.to.exist()
done()
})
it('returns undefined multiaddr from missing socket data', (done) => {
expect(getMultiaddr(badSocket))
.to.equal(undefined)
done()
})
it('returns undefined multiaddr from unparseable socket data', (done) => {
expect(getMultiaddr(badSocketData))
.to.equal(undefined)
done()
})
})

241
test/listen-dial.spec.js Normal file
View File

@ -0,0 +1,241 @@
/* eslint-env mocha */
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const TCP = require('../src')
const net = require('net')
const multiaddr = require('multiaddr')
const pipe = require('it-pipe')
const { collect, map } = require('streaming-iterables')
const isCI = process.env.CI
describe('listen', () => {
let tcp
beforeEach(() => {
tcp = new TCP()
})
it('close listener with connections, through timeout', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const listener = tcp.createListener((conn) => {
pipe(conn, conn)
})
await listener.listen(mh)
const socket1 = net.connect(9090)
const socket2 = net.connect(9090)
socket1.write('Some data that is never handled')
socket1.end()
socket1.on('error', () => {})
socket2.on('error', () => {})
await new Promise((resolve) => {
socket1.on('connect', async () => {
await listener.close({ timeout: 100 })
resolve()
})
})
})
it('listen on port 0', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/0')
const listener = tcp.createListener((conn) => {})
await listener.listen(mh)
await listener.close()
})
it('listen on IPv6 addr', async () => {
if (isCI) {
return
}
const mh = multiaddr('/ip6/::/tcp/9090')
const listener = tcp.createListener((conn) => {})
await listener.listen(mh)
await listener.close()
})
it('listen on any Interface', async () => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090')
const listener = tcp.createListener((conn) => {})
await listener.listen(mh)
await listener.close()
})
it('getAddrs', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090')
const listener = tcp.createListener((conn) => {})
await listener.listen(mh)
const multiaddrs = listener.getAddrs()
expect(multiaddrs.length).to.equal(1)
expect(multiaddrs[0]).to.deep.equal(mh)
await listener.close()
})
it('getAddrs on port 0 listen', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/0')
const listener = tcp.createListener((conn) => {})
await listener.listen(mh)
const multiaddrs = listener.getAddrs()
expect(multiaddrs.length).to.equal(1)
await listener.close()
})
it('getAddrs from listening on 0.0.0.0', async () => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090')
const listener = tcp.createListener((conn) => {})
await listener.listen(mh)
const multiaddrs = listener.getAddrs()
expect(multiaddrs.length > 0).to.equal(true)
expect(multiaddrs[0].toString().indexOf('0.0.0.0')).to.equal(-1)
await listener.close()
})
it('getAddrs from listening on 0.0.0.0 and port 0', async () => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/0')
const listener = tcp.createListener((conn) => {})
await listener.listen(mh)
const multiaddrs = listener.getAddrs()
expect(multiaddrs.length > 0).to.equal(true)
expect(multiaddrs[0].toString().indexOf('0.0.0.0')).to.equal(-1)
await listener.close()
})
it('getAddrs preserves IPFS Id', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const listener = tcp.createListener((conn) => {})
await listener.listen(mh)
const multiaddrs = listener.getAddrs()
expect(multiaddrs.length).to.equal(1)
expect(multiaddrs[0]).to.deep.equal(mh)
await listener.close()
})
})
describe('dial', () => {
let tcp
let listener
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090')
beforeEach(async () => {
tcp = new TCP()
listener = tcp.createListener((conn) => {
pipe(
conn,
map((x) => Buffer.from(x.toString() + '!')),
conn
)
})
await listener.listen(ma)
})
afterEach(() => listener.close())
it('dial on IPv4', async () => {
const values = await pipe(
['hey'],
await tcp.dial(ma),
collect
)
expect(values).to.eql([Buffer.from('hey!')])
})
it('dial on IPv6', async () => {
if (isCI) {
return
}
const ma = multiaddr('/ip6/::/tcp/9066')
const listener = tcp.createListener((conn) => {
pipe(conn, conn)
})
await listener.listen(ma)
const values = await pipe(
['hey'],
await tcp.dial(ma),
collect
)
expect(values).to.be.eql([Buffer.from('hey')])
await listener.close()
})
it('dial and destroy on listener', async () => {
let handled
const handledPromise = new Promise((resolve) => {
handled = resolve
})
const ma = multiaddr('/ip6/::/tcp/0')
const listener = tcp.createListener(async (conn) => {
await pipe(
[],
conn
)
handled()
})
await listener.listen(ma)
const addrs = listener.getAddrs()
await pipe(await tcp.dial(addrs[0]))
await handledPromise
await listener.close()
})
it('dial and destroy on dialer', async () => {
if (isCI) {
return
}
let handled
const handledPromise = new Promise((resolve) => {
handled = resolve
})
const ma = multiaddr('/ip6/::/tcp/0')
const listener = tcp.createListener(async (conn) => {
// pull(conn, pull.onEnd(destroyed))
await pipe(conn)
handled()
})
await listener.listen(ma)
const addrs = listener.getAddrs()
await pipe(await tcp.dial(addrs[0]))
await handledPromise
await listener.close()
})
it('dial on IPv4 with IPFS Id', async () => {
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const conn = await tcp.dial(ma)
const res = await pipe(
['hey'],
conn,
collect
)
expect(res).to.be.eql([Buffer.from('hey!')])
})
})