Compare commits

...

39 Commits

Author SHA1 Message Date
562b3adf1f chore: release version v0.14.5 2020-04-28 14:46:01 +02:00
eac9129bc0 chore: update contributors 2020-04-28 14:46:01 +02:00
0fe0815514 fix: catch error from maConn.close (#128) 2020-04-28 14:34:37 +02:00
2bc2b36050 chore: release version v0.14.4 2020-02-24 18:22:49 +01:00
f4f3e8529b chore: update contributors 2020-02-24 18:22:48 +01:00
3a127fca4c chore(deps-dev): bump sinon from 8.1.1 to 9.0.0
Bumps [sinon](https://github.com/sinonjs/sinon) from 8.1.1 to 9.0.0.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v8.1.1...v9.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-19 08:21:22 -05:00
0c4269cdc2 chore(deps-dev): bump aegir from 20.6.1 to 21.0.2 (#125)
Bumps [aegir](https://github.com/ipfs/aegir) from 20.6.1 to 21.0.2.
- [Release notes](https://github.com/ipfs/aegir/releases)
- [Changelog](https://github.com/ipfs/aegir/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ipfs/aegir/compare/v20.6.1...v21.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 07:57:52 -05:00
40f4cb6bb7 chore(deps-dev): bump sinon from 7.5.0 to 8.1.1 (#120)
Bumps [sinon](https://github.com/sinonjs/sinon) from 7.5.0 to 8.1.1.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v7.5.0...v8.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-14 14:14:08 +01:00
62728769d3 fix: remove use of assert module (#123)
The polyfill is big, we can simulate it by throwing an Error and it doesn't work under React Native.
2020-02-14 14:13:03 +01:00
86db568cd8 fix: catch thrown maConn errors in listener (#122)
When upgrading sockets to MultiaddConnections, it's possible for an error to be thrown.
This can crash the application if a client disconnects prior to the listener
uprading the socket, as is likely occurring in #121. Errors will now be caught and logged
when attempting to upgrade the socket.
2020-02-14 14:12:30 +01:00
0c2d84e395 chore: release version v0.14.3
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2019-12-20 09:02:38 -08:00
13fb6ba6e4 chore: update contributors 2019-12-20 09:02:37 -08:00
21f87476f6 fix: transport should not handle connection if upgradeInbound throws (#119) 2019-12-20 08:33:36 -08:00
657d3e6f4a chore: release version v0.14.2
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2019-12-06 10:09:04 +01:00
162c605bf5 chore: update contributors 2019-12-06 10:09:03 +01:00
d76a1f27e7 feat: add path multiaddr support (#118)
* chore: update deps
* test: skip paths on windows
2019-12-06 09:42:43 +01:00
7702646cfa fix(log): log the bound port and host (#117) 2019-10-02 16:49:13 +02:00
7c977d3cb1 chore: use libp2p utils for ip port to multiaddr (#116) 2019-09-24 16:35:40 +02:00
524acbe372 chore: release version v0.14.1
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2019-09-20 13:43:27 +02:00
1b210e71c0 chore: update contributors 2019-09-20 13:43:27 +02:00
a385f0eb34 docs: add upgrader to readme example (#114) 2019-09-20 13:42:11 +02:00
605ee279ae fix: ensure timeline.close is set (#113)
* fix(test): dns4 isnt a valid tcp multiaddr

It should be resolved first

* fix: ensure timeline.close is set on the multiaddrConn

* chore: add docs to gitignore

They are generated and uploaded on release
2019-09-19 15:20:54 +02:00
872e15af46 chore: release version v0.14.0
License: MIT
Signed-off-by: Jacob Heun <jacobheun@gmail.com>
2019-09-16 17:27:16 +02:00
0aba4f8611 chore: update contributors 2019-09-16 17:27:15 +02:00
cf7d1b8501 feat: change api to async / await (#112)
BREAKING CHANGE: All places in the API that used callbacks are now replaced with async/await. The API has also been updated according to the latest `interface-transport` version, https://github.com/libp2p/interface-transport/tree/v0.6.0#api.
2019-09-16 17:19:47 +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
22 changed files with 875 additions and 864 deletions

47
.gitignore vendored
View File

@ -1,46 +1,5 @@
docs
**/node_modules/
**/*.log
test/repo-tests*
**/bundle.js
# yarn
yarn.lock
# Logs
logs
*.log
coverage
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
build
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules node_modules
lib
dist
test/test-data/go-ipfs-repo/LOCK
test/test-data/go-ipfs-repo/LOG
test/test-data/go-ipfs-repo/LOG.old
# while testing npm5
package-lock.json package-lock.json
coverage
.nyc_output
docs

View File

@ -1,32 +1,35 @@
# 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 language: node_js
cache: npm
sudo: false
matrix: stages:
include: - check
- node_js: 6 - test
env: CXX=g++-4.8 - cov
- node_js: 8
env: CXX=g++-4.8
# - node_js: stable
# env: CXX=g++-4.8
script: node_js:
- npm run lint - '10'
- npm run test - '12'
- npm run coverage
os:
- linux
- osx
- windows
before_script: before_script:
- export DISPLAY=:99.0 - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; fi
- sh -e /etc/init.d/xvfb start
after_success: script: npx nyc -s npm run test:node -- --bail
- npm run coverage-publish after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
addons: jobs:
firefox: 'latest' include:
apt: - stage: check
sources: os: linux
- ubuntu-toolchain-r-test script:
packages: - npx aegir build --bundlesize
- g++-4.8 - npx aegir dep-check
- npm run lint
notifications:
email: false

View File

@ -1,3 +1,99 @@
<a name="0.14.5"></a>
## [0.14.5](https://github.com/libp2p/js-libp2p-tcp/compare/v0.14.4...v0.14.5) (2020-04-28)
### Bug Fixes
* catch error from maConn.close ([#128](https://github.com/libp2p/js-libp2p-tcp/issues/128)) ([0fe0815](https://github.com/libp2p/js-libp2p-tcp/commit/0fe0815))
<a name="0.14.4"></a>
## [0.14.4](https://github.com/libp2p/js-libp2p-tcp/compare/v0.14.3...v0.14.4) (2020-02-24)
### Bug Fixes
* catch thrown maConn errors in listener ([#122](https://github.com/libp2p/js-libp2p-tcp/issues/122)) ([86db568](https://github.com/libp2p/js-libp2p-tcp/commit/86db568)), closes [#121](https://github.com/libp2p/js-libp2p-tcp/issues/121)
* remove use of assert module ([#123](https://github.com/libp2p/js-libp2p-tcp/issues/123)) ([6272876](https://github.com/libp2p/js-libp2p-tcp/commit/6272876))
<a name="0.14.3"></a>
## [0.14.3](https://github.com/libp2p/js-libp2p-tcp/compare/v0.14.2...v0.14.3) (2019-12-20)
### Bug Fixes
* transport should not handle connection if upgradeInbound throws ([#119](https://github.com/libp2p/js-libp2p-tcp/issues/119)) ([21f8747](https://github.com/libp2p/js-libp2p-tcp/commit/21f8747))
<a name="0.14.2"></a>
## [0.14.2](https://github.com/libp2p/js-libp2p-tcp/compare/v0.14.1...v0.14.2) (2019-12-06)
### Bug Fixes
* **log:** log the bound port and host ([#117](https://github.com/libp2p/js-libp2p-tcp/issues/117)) ([7702646](https://github.com/libp2p/js-libp2p-tcp/commit/7702646))
### Features
* add path multiaddr support ([#118](https://github.com/libp2p/js-libp2p-tcp/issues/118)) ([d76a1f2](https://github.com/libp2p/js-libp2p-tcp/commit/d76a1f2))
<a name="0.14.1"></a>
## [0.14.1](https://github.com/libp2p/js-libp2p-tcp/compare/v0.14.0...v0.14.1) (2019-09-20)
### Bug Fixes
* ensure timeline.close is set ([#113](https://github.com/libp2p/js-libp2p-tcp/issues/113)) ([605ee27](https://github.com/libp2p/js-libp2p-tcp/commit/605ee27))
<a name="0.14.0"></a>
# [0.14.0](https://github.com/libp2p/js-libp2p-tcp/compare/v0.13.1...v0.14.0) (2019-09-16)
### Features
* change api to async / await ([#112](https://github.com/libp2p/js-libp2p-tcp/issues/112)) ([cf7d1b8](https://github.com/libp2p/js-libp2p-tcp/commit/cf7d1b8))
### BREAKING CHANGES
* All places in the API that used callbacks are now replaced with async/await. The API has also been updated according to the latest `interface-transport` version, https://github.com/libp2p/interface-transport/tree/v0.6.0#api.
<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> <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) # [0.12.0](https://github.com/libp2p/js-libp2p-tcp/compare/v0.11.6...v0.12.0) (2018-04-05)

View File

@ -1,19 +1,23 @@
# js-libp2p-tcp # 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/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) [![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![Build Status](https://travis-ci.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://travis-ci.org/libp2p/js-libp2p-tcp) [![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
[![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) [![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) [![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) [![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-transport/master/img/badge.png)](https://github.com/libp2p/interface-transport) [![](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) [![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png)](https://github.com/libp2p/interface-connection)
> 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 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.
## Lead Maintainer
[Jacob Heun](https://github.com/jacobheun)
## Table of Contents ## Table of Contents
@ -37,37 +41,38 @@
```js ```js
const TCP = require('libp2p-tcp') const TCP = require('libp2p-tcp')
const multiaddr = require('multiaddr') const multiaddr = require('multiaddr')
const pull = require('pull-stream') const pipe = require('it-pipe')
const { collect } = require('streaming-iterables')
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') // A simple upgrader that just returns the MultiaddrConnection
const upgrader = {
upgradeInbound: maConn => maConn,
upgradeOutbound: maConn => maConn
}
const tcp = new TCP() const tcp = new TCP({ upgrader })
const listener = tcp.createListener((socket) => { const listener = tcp.createListener((socket) => {
console.log('new connection opened') console.log('new connection opened')
pull( pipe(
pull.values(['hello']), ['hello'],
socket socket
) )
}) })
listener.listen(mh, () => { const addr = multiaddr('/ip4/127.0.0.1/tcp/9090')
console.log('listening') await listener.listen(addr)
console.log('listening')
pull( const socket = await tcp.dial(addr)
tcp.dial(mh), const values = await pipe(
pull.collect((err, values) => { socket,
if (!err) { collect
console.log(`Value: ${values.toString()}`) )
} else { console.log(`Value: ${values.toString()}`)
console.log(`Error: ${err}`)
}
// Close connection after reading // Close connection after reading
listener.close() await listener.close()
}),
)
})
``` ```
Outputs: Outputs:
@ -84,12 +89,12 @@ Value: hello
[![](https://raw.githubusercontent.com/libp2p/interface-transport/master/img/badge.png)](https://github.com/libp2p/interface-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`
`/ip4/127.0.0.1/tcp/4001/ipfs/QmHash` `/ip4/127.0.0.1/tcp/4001/ipfs/QmHash`
Both for dialing and listening. (both for dialing and listening)
### Connection ### Connection

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,20 @@
{ {
"name": "libp2p-tcp", "name": "libp2p-tcp",
"version": "0.12.0", "version": "0.14.5",
"description": "Node.js implementation of the TCP module that libp2p uses, which implements the interface-connection and interface-transport interfaces", "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", "main": "src/index.js",
"scripts": { "scripts": {
"lint": "aegir lint", "lint": "aegir lint",
"test": "aegir test -t node", "test": "aegir test -t node",
"test:node": "aegir test -t node",
"release": "aegir release -t node --no-build", "release": "aegir release -t node --no-build",
"release-minor": "aegir release -t node --type minor --no-build", "release-minor": "aegir release -t node --type minor --no-build",
"release-major": "aegir-release -t node --type major --no-build", "release-major": "aegir-release -t node --type major --no-build",
"coverage": "aegir coverage", "coverage": "nyc --reporter=text --reporter=lcov npm run test:node"
"coverage-publish": "aegir coverage --provider coveralls"
}, },
"pre-push": [ "pre-push": [
"lint", "lint"
"test"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
@ -23,7 +23,6 @@
"keywords": [ "keywords": [
"IPFS" "IPFS"
], ],
"author": "David Dias <daviddias@ipfs.io>",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/libp2p/js-libp2p-tcp/issues" "url": "https://github.com/libp2p/js-libp2p-tcp/issues"
@ -34,39 +33,41 @@
"npm": ">=3.0.0" "npm": ">=3.0.0"
}, },
"devDependencies": { "devDependencies": {
"aegir": "^13.0.6", "aegir": "^21.0.2",
"chai": "^4.1.2", "chai": "^4.2.0",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"interface-transport": "~0.3.6", "libp2p-interfaces": "^0.2.0",
"lodash.isfunction": "^3.0.9", "it-pipe": "^1.1.0",
"pre-commit": "^1.2.2", "sinon": "^9.0.0",
"pull-stream": "^3.6.7" "streaming-iterables": "^4.1.1"
}, },
"dependencies": { "dependencies": {
"abortable-iterator": "^3.0.0",
"class-is": "^1.1.0", "class-is": "^1.1.0",
"debug": "^3.1.0", "debug": "^4.1.1",
"interface-connection": "~0.3.2", "err-code": "^2.0.0",
"ip-address": "^5.8.9", "libp2p-utils": "~0.1.0",
"lodash.includes": "^4.3.0", "mafmt": "^7.0.0",
"lodash.isfunction": "^3.0.9", "multiaddr": "^7.2.1",
"mafmt": "^6.0.0", "stream-to-it": "^0.2.0"
"multiaddr": "^4.0.0",
"once": "^1.4.0",
"stream-to-pull-stream": "^1.7.2"
}, },
"contributors": [ "contributors": [
"Alan Shaw <alan@tableflip.io>",
"David Dias <daviddias.p@gmail.com>", "David Dias <daviddias.p@gmail.com>",
"Jacob Heun <jacobheun@gmail.com>",
"Stephen Whitmore <stephen.whitmore@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Vasco Santos <vasco.santos@moxy.studio>",
"Richard Littauer <richard.littauer@gmail.com>",
"Tom White <tomtinkerer@gmail.com>",
"Alan Shaw <alan@tableflip.io>",
"Pedro Teixeira <i@pgte.me>",
"Prashanth Chandra <coolshanth94@gmail.com>",
"João Antunes <j.goncalo.antunes@gmail.com>",
"Alex Potsides <alex@achingbrain.net>",
"Diogo Silva <fsdiogo@gmail.com>", "Diogo Silva <fsdiogo@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>", "Dmitriy Ryajov <dryajov@gmail.com>",
"Drew Stone <drewstone329@gmail.com>", "Drew Stone <drewstone329@gmail.com>",
"Evan Schwartz <evan.mark.schwartz@gmail.com>", "Evan Schwartz <evan.mark.schwartz@gmail.com>",
"Friedel Ziegelmayer <dignifiedquire@gmail.com>", "Linus Unnebäck <linus@folkdatorn.se>"
"Greenkeeper <support@greenkeeper.io>",
"João Antunes <j.goncalo.antunes@gmail.com>",
"Pedro Teixeira <i@pgte.me>",
"Prashanth Chandra <coolshanth94@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>",
"Stephen Whitmore <stephen.whitmore@gmail.com>"
] ]
} }

8
src/constants.js Normal file
View File

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

View File

@ -1,28 +0,0 @@
'use strict'
const multiaddr = require('multiaddr')
const Address6 = require('ip-address').Address6
module.exports = (socket) => {
let ma
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 {
ma = multiaddr('/ip4/' + socket.remoteAddress +
'/tcp/' + socket.remotePort)
}
return ma
}

View File

@ -1,83 +1,139 @@
'use strict' 'use strict'
const net = require('net') const net = require('net')
const toPull = require('stream-to-pull-stream')
const mafmt = require('mafmt') const mafmt = require('mafmt')
const withIs = require('class-is') const withIs = require('class-is')
const includes = require('lodash.includes') const errCode = require('err-code')
const isFunction = require('lodash.isfunction') const log = require('debug')('libp2p:tcp')
const Connection = require('interface-connection').Connection const toConnection = require('./socket-to-conn')
const once = require('once')
const debug = require('debug')
const log = debug('libp2p:tcp:dial')
const createListener = require('./listener') const createListener = require('./listener')
const { multiaddrToNetConfig } = require('./utils')
const { AbortError } = require('abortable-iterator')
const { CODE_CIRCUIT, CODE_P2P } = require('./constants')
function noop () {} /**
* @class TCP
*/
class TCP { class TCP {
dial (ma, options, callback) { /**
if (isFunction(options)) { * @constructor
callback = options * @param {object} options
options = {} * @param {Upgrader} options.upgrader
} */
constructor ({ upgrader }) {
callback = once(callback || noop) if (!upgrader) {
throw new Error('An upgrader must be provided. See https://github.com/libp2p/interface-transport#upgrader.')
const cOpts = ma.toOptions()
log('Connecting to %s %s', cOpts.port, cOpts.host)
const rawSocket = net.connect(cOpts)
rawSocket.once('timeout', () => {
log('timeout')
rawSocket.emit('error', new Error('Timeout'))
})
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])
} }
this._upgrader = upgrader
}
/**
* @async
* @param {Multiaddr} ma
* @param {object} options
* @param {AbortSignal} options.signal Used to abort dial requests
* @returns {Connection} An upgraded Connection
*/
async dial (ma, options) {
options = options || {}
const socket = await this._connect(ma, options)
const maConn = toConnection(socket, { remoteAddr: ma, signal: options.signal })
log('new outbound connection %s', maConn.remoteAddr)
const conn = await this._upgrader.upgradeOutbound(maConn)
log('outbound connection %s upgraded', maConn.remoteAddr)
return conn return conn
} }
/**
* @private
* @param {Multiaddr} ma
* @param {object} options
* @param {AbortSignal} options.signal Used to abort dial requests
* @returns {Promise<Socket>} Resolves a TCP Socket
*/
_connect (ma, options = {}) {
if (options.signal && options.signal.aborted) {
throw new AbortError()
}
return new Promise((resolve, reject) => {
const start = Date.now()
const cOpts = multiaddrToNetConfig(ma)
log('dialing %j', cOpts)
const rawSocket = net.connect(cOpts)
const onError = err => {
err.message = `connection error ${cOpts.host}:${cOpts.port}: ${err.message}`
done(err)
}
const onTimeout = () => {
log('connnection timeout %s:%s', cOpts.host, cOpts.port)
const err = errCode(new Error(`connection timeout after ${Date.now() - start}ms`), 'ERR_CONNECT_TIMEOUT')
// Note: this will result in onError() being called
rawSocket.emit('error', err)
}
const onConnect = () => {
log('connection opened %j', cOpts)
done()
}
const onAbort = () => {
log('connection aborted %j', cOpts)
rawSocket.destroy()
done(new AbortError())
}
const done = err => {
rawSocket.removeListener('error', onError)
rawSocket.removeListener('timeout', onTimeout)
rawSocket.removeListener('connect', onConnect)
options.signal && options.signal.removeEventListener('abort', onAbort)
if (err) return reject(err)
resolve(rawSocket)
}
rawSocket.on('error', onError)
rawSocket.on('timeout', onTimeout)
rawSocket.on('connect', onConnect)
options.signal && options.signal.addEventListener('abort', onAbort)
})
}
/**
* Creates a TCP listener. The provided `handler` function will be called
* anytime a new incoming Connection has been successfully upgraded via
* `upgrader.upgradeInbound`.
* @param {*} [options]
* @param {function(Connection)} handler
* @returns {Listener} A TCP listener
*/
createListener (options, handler) { createListener (options, handler) {
if (isFunction(options)) { if (typeof options === 'function') {
handler = options handler = options
options = {} options = {}
} }
options = options || {}
handler = handler || noop return createListener({ handler, upgrader: this._upgrader }, options)
return createListener(handler)
} }
/**
* Takes a list of `Multiaddr`s and returns only valid TCP addresses
* @param {Multiaddr[]} multiaddrs
* @returns {Multiaddr[]} Valid TCP multiaddrs
*/
filter (multiaddrs) { filter (multiaddrs) {
if (!Array.isArray(multiaddrs)) { multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
multiaddrs = [multiaddrs]
}
return multiaddrs.filter((ma) => { return multiaddrs.filter(ma => {
if (includes(ma.protoNames(), 'p2p-circuit')) { if (ma.protoCodes().includes(CODE_CIRCUIT)) {
return false return false
} }
if (includes(ma.protoNames(), 'ipfs')) { return mafmt.TCP.matches(ma.decapsulateCode(CODE_P2P))
ma = ma.decapsulate('ipfs')
}
return mafmt.TCP.matches(ma)
}) })
} }
} }

View File

@ -1,147 +1,122 @@
'use strict' '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 net = require('net')
const toPull = require('stream-to-pull-stream') const EventEmitter = require('events')
const EventEmitter = require('events').EventEmitter
const debug = require('debug') const debug = require('debug')
const log = debug('libp2p:tcp:listen') const log = debug('libp2p:tcp:listener')
log.error = debug('libp2p:tcp:listener:error')
const getMultiaddr = require('./get-multiaddr') const toConnection = require('./socket-to-conn')
const { CODE_P2P } = require('./constants')
const {
getMultiaddrs,
multiaddrToNetConfig
} = require('./utils')
const IPFS_CODE = 421 /**
const CLOSE_TIMEOUT = 2000 * Attempts to close the given maConn. If a failure occurs, it will be logged.
* @private
* @param {MultiaddrConnection} maConn
*/
async function attemptClose (maConn) {
try {
maConn && await maConn.close()
} catch (err) {
log.error('an error occurred closing the connection', err)
}
}
function noop () {} module.exports = ({ handler, upgrader }, options) => {
module.exports = (handler) => {
const listener = new EventEmitter() const listener = new EventEmitter()
const server = net.createServer((socket) => { const server = net.createServer(async socket => {
// Avoid uncaught errors cause by unstable connections // Avoid uncaught errors caused by unstable connections
socket.on('error', noop) socket.on('error', err => log('socket error', err))
const addr = getMultiaddr(socket) let maConn
log('new connection', addr.toString()) let conn
try {
const s = toPull.duplex(socket) maConn = toConnection(socket, { listeningAddr })
log('new inbound connection %s', maConn.remoteAddr)
s.getObservedAddrs = (cb) => { conn = await upgrader.upgradeInbound(maConn)
cb(null, [addr]) } catch (err) {
log.error('inbound connection failed', err)
return attemptClose(maConn)
} }
trackSocket(server, socket) log('inbound connection %s upgraded', maConn.remoteAddr)
const conn = new Connection(s) trackConn(server, maConn)
handler(conn)
if (handler) handler(conn)
listener.emit('connection', conn) listener.emit('connection', conn)
}) })
server.on('listening', () => listener.emit('listening')) server
server.on('error', (err) => listener.emit('error', err)) .on('listening', () => listener.emit('listening'))
server.on('close', () => listener.emit('close')) .on('error', err => listener.emit('error', err))
.on('close', () => listener.emit('close'))
// Keep track of open connections to destroy in case of timeout // Keep track of open connections to destroy in case of timeout
server.__connections = {} server.__connections = []
listener.close = (options, callback) => { listener.close = () => {
if (typeof options === 'function') { if (!server.listening) return
callback = options
options = {}
}
callback = callback || noop
options = options || {}
const timeout = setTimeout(() => { return new Promise((resolve, reject) => {
log('unable to close graciously, destroying conns') server.__connections.forEach(maConn => attemptClose(maConn))
Object.keys(server.__connections).forEach((key) => { server.close(err => err ? reject(err) : resolve())
log('destroying %s', key)
server.__connections[key].destroy()
})
}, options.timeout || CLOSE_TIMEOUT)
server.close(callback)
server.once('close', () => {
clearTimeout(timeout)
}) })
} }
let ipfsId let peerId, listeningAddr
let listeningAddr
listener.listen = (ma, callback) => { listener.listen = ma => {
listeningAddr = ma listeningAddr = ma
if (includes(ma.protoNames(), 'ipfs')) { peerId = ma.getPeerId()
ipfsId = getIpfsId(ma)
listeningAddr = ma.decapsulate('ipfs') if (peerId) {
listeningAddr = ma.decapsulateCode(CODE_P2P)
} }
const lOpts = listeningAddr.toOptions() return new Promise((resolve, reject) => {
log('Listening on %s %s', lOpts.port, lOpts.host) const options = multiaddrToNetConfig(listeningAddr)
return server.listen(lOpts.port, lOpts.host, callback) server.listen(options, err => {
if (err) return reject(err)
log('Listening on %s', server.address())
resolve()
})
})
} }
listener.getAddrs = (callback) => { listener.getAddrs = () => {
const multiaddrs = [] let addrs = []
const address = server.address() const address = server.address()
if (!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 // Because TCP will only return the IPv6 version
// we need to capture from the passed multiaddr // we need to capture from the passed multiaddr
if (listeningAddr.toString().indexOf('ip4') !== -1) { if (listeningAddr.toString().startsWith('/ip4')) {
let m = listeningAddr.decapsulate('tcp') addrs = addrs.concat(getMultiaddrs('ip4', address.address, address.port))
m = m.encapsulate('/tcp/' + address.port) } else if (address.family === 'IPv6') {
if (ipfsId) { addrs = addrs.concat(getMultiaddrs('ip6', address.address, address.port))
m = m.encapsulate('/ipfs/' + ipfsId)
}
if (m.toString().indexOf('0.0.0.0') !== -1) {
const netInterfaces = os.networkInterfaces()
Object.keys(netInterfaces).forEach((niKey) => {
netInterfaces[niKey].forEach((ni) => {
if (ni.family === 'IPv4') {
multiaddrs.push(multiaddr(m.toString().replace('0.0.0.0', ni.address)))
}
})
})
} else {
multiaddrs.push(m)
}
} }
if (address.family === 'IPv6') { return addrs.map(ma => peerId ? ma.encapsulate(`/p2p/${peerId}`) : ma)
let ma = multiaddr('/ip6/' + address.address + '/tcp/' + address.port)
if (ipfsId) {
ma = ma.encapsulate('/ipfs/' + ipfsId)
}
multiaddrs.push(ma)
}
callback(null, multiaddrs)
} }
return listener return listener
} }
function getIpfsId (ma) { function trackConn (server, maConn) {
return ma.stringTuples().filter((tuple) => { server.__connections.push(maConn)
return tuple[0] === IPFS_CODE
})[0][1]
}
function trackSocket (server, socket) { const untrackConn = () => {
const key = `${socket.remoteAddress}:${socket.remotePort}` server.__connections = server.__connections.filter(c => c !== maConn)
server.__connections[key] = socket }
socket.on('close', () => { maConn.conn.once('close', untrackConn)
delete server.__connections[key]
})
} }

101
src/socket-to-conn.js Normal file
View File

@ -0,0 +1,101 @@
'use strict'
const abortable = require('abortable-iterator')
const log = require('debug')('libp2p:tcp:socket')
const toIterable = require('stream-to-it')
const toMultiaddr = require('libp2p-utils/src/ip-port-to-multiaddr')
const { CLOSE_TIMEOUT } = require('./constants')
// Convert a socket into a MultiaddrConnection
// https://github.com/libp2p/interface-transport#multiaddrconnection
module.exports = (socket, options) => {
options = options || {}
// Check if we are connected on a unix path
if (options.listeningAddr && options.listeningAddr.getPath()) {
options.remoteAddr = options.listeningAddr
}
if (options.remoteAddr && options.remoteAddr.getPath()) {
options.localAddr = options.remoteAddr
}
const { sink, source } = toIterable.duplex(socket)
const maConn = {
async sink (source) {
if (options.signal) {
source = abortable(source, options.signal)
}
try {
await sink((async function * () {
for await (const chunk of source) {
// Convert BufferList to Buffer
yield Buffer.isBuffer(chunk) ? chunk : chunk.slice()
}
})())
} catch (err) {
// If aborted we can safely ignore
if (err.type !== 'aborted') {
// If the source errored the socket will already have been destroyed by
// toIterable.duplex(). If the socket errored it will already be
// destroyed. There's nothing to do here except log the error & return.
log(err)
}
}
},
source: options.signal ? abortable(source, options.signal) : source,
conn: socket,
localAddr: options.localAddr || toMultiaddr(socket.localAddress, socket.localPort),
// If the remote address was passed, use it - it may have the peer ID encapsulated
remoteAddr: options.remoteAddr || toMultiaddr(socket.remoteAddress, socket.remotePort),
timeline: { open: Date.now() },
close () {
if (socket.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 { host, port } = maConn.remoteAddr.toOptions()
log('timeout closing socket to %s:%s after %dms, destroying it manually',
host, port, Date.now() - start)
if (socket.destroyed) {
log('%s:%s is already destroyed', host, port)
} else {
socket.destroy()
}
resolve()
}, CLOSE_TIMEOUT)
socket.once('close', () => clearTimeout(timeout))
socket.end(err => {
maConn.timeline.close = Date.now()
if (err) return reject(err)
resolve()
})
})
}
}
socket.once('close', () => {
// In instances where `close` was not explicitly called,
// such as an iterable stream ending, ensure we have set the close
// timeline
if (!maConn.timeline.close) {
maConn.timeline.close = Date.now()
}
})
return maConn
}

46
src/utils.js Normal file
View File

@ -0,0 +1,46 @@
'use strict'
const multiaddr = require('multiaddr')
const os = require('os')
const { resolve } = require('path')
const ProtoFamily = { ip4: 'IPv4', ip6: 'IPv6' }
function multiaddrToNetConfig (addr) {
const listenPath = addr.getPath()
// unix socket listening
if (listenPath) {
return resolve(listenPath)
}
// tcp listening
return addr.toOptions()
}
function getMultiaddrs (proto, ip, port) {
const toMa = ip => multiaddr(`/${proto}/${ip}/tcp/${port}`)
return (isAnyAddr(ip) ? getNetworkAddrs(ProtoFamily[proto]) : [ip]).map(toMa)
}
function isAnyAddr (ip) {
return ['0.0.0.0', '::'].includes(ip)
}
/**
* @private
* @param {string} family One of ['IPv6', 'IPv4']
* @returns {string[]} an array of ip address strings
*/
function getNetworkAddrs (family) {
return Object.values(os.networkInterfaces()).reduce((addresses, netAddrs) => {
netAddrs.forEach(netAddr => {
// Add the ip of each matching network interface
if (netAddr.family === family) addresses.push(netAddr.address)
})
return addresses
}, [])
}
module.exports = {
multiaddrToNetConfig,
isAnyAddr,
getMultiaddrs
}

View File

@ -1,23 +1,42 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const tests = require('interface-transport') const sinon = require('sinon')
const tests = require('libp2p-interfaces/src/transport/tests')
const multiaddr = require('multiaddr') const multiaddr = require('multiaddr')
const net = require('net')
const TCP = require('../src') const TCP = require('../src')
describe('interface-transport compliance', () => { describe('interface-transport compliance', () => {
tests({ tests({
setup (cb) { setup ({ upgrader }) {
let tcp = new TCP() const tcp = new TCP({ upgrader })
const addrs = [ const addrs = [
multiaddr('/ip4/127.0.0.1/tcp/9091'), multiaddr('/ip4/127.0.0.1/tcp/9091'),
multiaddr('/ip4/127.0.0.1/tcp/9092'), multiaddr('/ip4/127.0.0.1/tcp/9092'),
multiaddr('/ip4/127.0.0.1/tcp/9093') multiaddr('/ip4/127.0.0.1/tcp/9093')
] ]
cb(null, tcp, addrs)
}, // Used by the dial tests to simulate a delayed connect
teardown (cb) { const connector = {
cb() 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,121 +0,0 @@
/* 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')
const multiaddr = require('multiaddr')
const Connection = require('interface-connection').Connection
describe('Connection Wrap', () => {
let tcp
let listener
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090')
beforeEach((done) => {
tcp = new TCP()
listener = tcp.createListener((conn) => {
pull(conn, conn)
})
listener.on('listening', done)
listener.listen(ma)
})
afterEach((done) => {
listener.close(done)
})
it('simple wrap', (done) => {
const conn = tcp.dial(ma)
conn.setPeerInfo('peerInfo')
const connWrap = new Connection(conn)
pull(
pull.values(['hey']),
connWrap,
pull.collect((err, chunks) => {
expect(err).to.not.exist()
expect(chunks).to.be.eql([Buffer.from('hey')])
connWrap.getPeerInfo((err, peerInfo) => {
expect(err).to.not.exist()
expect(peerInfo).to.equal('peerInfo')
done()
})
})
)
})
it('buffer wrap', (done) => {
const conn = tcp.dial(ma)
const connWrap = new Connection()
pull(
pull.values(['hey']),
connWrap,
pull.collect((err, chunks) => {
expect(err).to.not.exist()
expect(chunks).to.be.eql([Buffer.from('hey')])
done()
})
)
connWrap.setInnerConn(conn)
})
it('overload wrap', (done) => {
const conn = tcp.dial(ma)
const connWrap = new Connection(conn)
connWrap.getPeerInfo = (callback) => {
callback(null, 'none')
}
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.exist()
})
connWrap.getPeerInfo((err, peerInfo) => {
expect(err).to.not.exist()
expect(peerInfo).to.equal('none')
})
pull(
pull.values(['hey']),
connWrap,
pull.collect((err, chunks) => {
expect(err).to.not.exist()
expect(chunks).to.be.eql([Buffer.from('hey')])
done()
})
)
})
it('dial error', (done) => {
tcp.dial(multiaddr('/ip4/127.0.0.1/tcp/22234'), (err) => {
expect(err).to.exist()
done()
})
})
it('matryoshka wrap', (done) => {
const conn = tcp.dial(ma)
const connWrap1 = new Connection(conn)
const connWrap2 = new Connection(connWrap1)
const connWrap3 = new Connection(connWrap2)
conn.getPeerInfo = (callback) => {
callback(null, 'inner doll')
}
pull(
pull.values(['hey']),
connWrap3,
pull.collect((err, chunks) => {
expect(err).to.not.exist()
expect(chunks).to.eql([Buffer.from('hey')])
connWrap3.getPeerInfo((err, peerInfo) => {
expect(err).to.not.exist()
expect(peerInfo).to.equal('inner doll')
done()
})
})
)
})
})

View File

@ -1,7 +1,6 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const pull = require('pull-stream')
const chai = require('chai') const chai = require('chai')
const dirtyChai = require('dirty-chai') const dirtyChai = require('dirty-chai')
const expect = chai.expect const expect = chai.expect
@ -9,103 +8,49 @@ chai.use(dirtyChai)
const TCP = require('../src') const TCP = require('../src')
const multiaddr = require('multiaddr') const multiaddr = require('multiaddr')
describe('valid Connection', () => { describe('valid localAddr and remoteAddr', () => {
let tcp let tcp
const mockUpgrader = {
upgradeInbound: maConn => maConn,
upgradeOutbound: maConn => maConn
}
beforeEach(() => { beforeEach(() => {
tcp = new TCP() tcp = new TCP({ upgrader: mockUpgrader })
}) })
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090') const ma = multiaddr('/ip4/127.0.0.1/tcp/0')
it('get observed addrs', (done) => { it('should resolve port 0', async () => {
let dialerObsAddrs // 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 = conn => handled(conn)
expect(conn).to.exist()
conn.getObservedAddrs((err, addrs) => {
expect(err).to.not.exist()
dialerObsAddrs = addrs
pull(pull.empty(), conn)
})
})
listener.listen(ma, () => { // Create a listener with the handler
const conn = tcp.dial(ma) const listener = tcp.createListener(handler)
pull(
conn,
pull.onEnd(endHandler)
)
function endHandler () { // Listen on the multi-address
conn.getObservedAddrs((err, addrs) => { await listener.listen(ma)
expect(err).to.not.exist()
pull(pull.empty(), conn)
closeAndAssert(listener, addrs)
})
}
function closeAndAssert (listener, addrs) { const localAddrs = listener.getAddrs()
listener.close(() => { expect(localAddrs.length).to.equal(1)
expect(addrs[0]).to.deep.equal(ma)
expect(dialerObsAddrs.length).to.equal(1)
done()
})
}
})
})
it('get Peer Info', (done) => { // Dial to that address
const listener = tcp.createListener((conn) => { const dialerConn = await tcp.dial(localAddrs[0])
expect(conn).to.exist()
conn.getPeerInfo((err, peerInfo) => {
expect(err).to.exist()
expect(peerInfo).to.not.exist()
pull(pull.empty(), conn)
})
})
listener.listen(ma, () => { // Wait for the incoming dial to be handled
const conn = tcp.dial(ma) const listenerConn = await handlerPromise
pull(conn, pull.onEnd(endHandler)) // Close the listener
await listener.close()
function endHandler () { expect(dialerConn.localAddr.toString())
conn.getPeerInfo((err, peerInfo) => { .to.equal(listenerConn.remoteAddr.toString())
expect(err).to.exist()
expect(peerInfo).to.not.exist()
listener.close(done) expect(dialerConn.remoteAddr.toString())
}) .to.equal(listenerConn.localAddr.toString())
}
})
})
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

@ -1,15 +0,0 @@
/* 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')
describe('Constructor', () => {
it('create an instance', () => {
const tcp = new TCP()
expect(tcp).to.exist()
})
})

View File

@ -15,7 +15,7 @@ describe('filter addrs', () => {
let tcp let tcp
before(() => { before(() => {
tcp = new TCP() tcp = new TCP({ upgrader: {} })
}) })
it('filter valid addrs for this transport', () => { it('filter valid addrs for this transport', () => {
@ -25,9 +25,11 @@ describe('filter addrs', () => {
const ma4 = multiaddr(base + '/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw') const ma4 = multiaddr(base + '/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const ma5 = multiaddr(base + '/tcp/9090/http' + ipfs) const ma5 = multiaddr(base + '/tcp/9090/http' + ipfs)
const ma6 = multiaddr('/ip4/127.0.0.1/tcp/9090/p2p-circuit' + 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([ma1, ma2, ma3, ma4, ma5, ma6]) const valid = tcp.filter([ma1, ma2, ma3, ma4, ma5, ma6, ma7, ma8])
expect(valid.length).to.equal(2) expect(valid.length).to.equal(4)
expect(valid[0]).to.deep.equal(ma1) expect(valid[0]).to.deep.equal(ma1)
expect(valid[1]).to.deep.equal(ma4) expect(valid[1]).to.deep.equal(ma4)
}) })

View File

@ -1,260 +0,0 @@
/* 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')
const net = require('net')
const multiaddr = require('multiaddr')
const isCI = process.env.CI
describe('listen', () => {
let tcp
beforeEach(() => {
tcp = new TCP()
})
it('close listener with connections, through timeout', (done) => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const listener = tcp.createListener((conn) => {
pull(conn, conn)
})
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', () => {})
socket1.on('connect', () => {
listener.close(done)
})
})
})
it('listen on port 0', (done) => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/0')
const listener = tcp.createListener((conn) => {})
listener.listen(mh, () => {
listener.close(done)
})
})
it('listen on IPv6 addr', (done) => {
if (isCI) { return done() }
const mh = multiaddr('/ip6/::/tcp/9090')
const listener = tcp.createListener((conn) => {})
listener.listen(mh, () => {
listener.close(done)
})
})
it('listen on any Interface', (done) => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090')
const listener = tcp.createListener((conn) => {})
listener.listen(mh, () => {
listener.close(done)
})
})
it('getAddrs', (done) => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090')
const listener = tcp.createListener((conn) => {})
listener.listen(mh, () => {
listener.getAddrs((err, multiaddrs) => {
expect(err).to.not.exist()
expect(multiaddrs.length).to.equal(1)
expect(multiaddrs[0]).to.deep.equal(mh)
listener.close(done)
})
})
})
it('getAddrs on port 0 listen', (done) => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/0')
const listener = tcp.createListener((conn) => {})
listener.listen(mh, () => {
listener.getAddrs((err, multiaddrs) => {
expect(err).to.not.exist()
expect(multiaddrs.length).to.equal(1)
listener.close(done)
})
})
})
it('getAddrs from listening on 0.0.0.0', (done) => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090')
const listener = tcp.createListener((conn) => {})
listener.listen(mh, () => {
listener.getAddrs((err, multiaddrs) => {
expect(err).to.not.exist()
expect(multiaddrs.length > 0).to.equal(true)
expect(multiaddrs[0].toString().indexOf('0.0.0.0')).to.equal(-1)
listener.close(done)
})
})
})
it('getAddrs from listening on 0.0.0.0 and port 0', (done) => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/0')
const listener = tcp.createListener((conn) => {})
listener.listen(mh, () => {
listener.getAddrs((err, multiaddrs) => {
expect(err).to.not.exist()
expect(multiaddrs.length > 0).to.equal(true)
expect(multiaddrs[0].toString().indexOf('0.0.0.0')).to.equal(-1)
listener.close(done)
})
})
})
it('getAddrs preserves IPFS Id', (done) => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const listener = tcp.createListener((conn) => {})
listener.listen(mh, () => {
listener.getAddrs((err, multiaddrs) => {
expect(err).to.not.exist()
expect(multiaddrs.length).to.equal(1)
expect(multiaddrs[0]).to.deep.equal(mh)
listener.close(done)
})
})
})
})
describe('dial', () => {
let tcp
let listener
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090')
beforeEach((done) => {
tcp = new TCP()
listener = tcp.createListener((conn) => {
pull(
conn,
pull.map((x) => Buffer.from(x.toString() + '!')),
conn
)
})
listener.listen(ma, done)
})
afterEach((done) => {
listener.close(done)
})
it('dial on IPv4', (done) => {
pull(
pull.values(['hey']),
tcp.dial(ma),
pull.collect((err, values) => {
expect(err).to.not.exist()
expect(values).to.eql([Buffer.from('hey!')])
done()
})
)
})
it('dial to non existent listener', (done) => {
const ma = multiaddr('/ip4/127.0.0.1/tcp/8989')
pull(
tcp.dial(ma),
pull.onEnd((err) => {
expect(err).to.exist()
done()
})
)
})
it('dial on IPv6', (done) => {
if (isCI) { return done() }
const ma = multiaddr('/ip6/::/tcp/9066')
const listener = tcp.createListener((conn) => {
pull(conn, conn)
})
listener.listen(ma, () => {
pull(
pull.values(['hey']),
tcp.dial(ma),
pull.collect((err, values) => {
expect(err).to.not.exist()
expect(values).to.be.eql([Buffer.from('hey')])
listener.close(done)
})
)
})
})
// TODO: figure out why is this failing
it.skip('dial and destroy on listener', (done) => {
let count = 0
const closed = ++count === 2 ? finish() : null
const ma = multiaddr('/ip6/::/tcp/9067')
const listener = tcp.createListener((conn) => {
pull(
pull.empty(),
conn,
pull.onEnd(closed)
)
})
listener.listen(ma, () => {
pull(tcp.dial(ma), pull.onEnd(closed))
})
function finish () {
listener.close(done)
}
})
it('dial and destroy on dialer', (done) => {
if (isCI) { return done() }
let count = 0
const destroyed = () => ++count === 2 ? finish() : null
const ma = multiaddr('/ip6/::/tcp/9068')
const listener = tcp.createListener((conn) => {
pull(conn, pull.onEnd(destroyed))
})
listener.listen(ma, () => {
pull(
pull.empty(),
tcp.dial(ma),
pull.onEnd(destroyed)
)
})
function finish () {
listener.close(done)
}
})
it('dial on IPv4 with IPFS Id', (done) => {
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
const conn = tcp.dial(ma)
pull(
pull.values(['hey']),
conn,
pull.collect((err, res) => {
expect(err).to.not.exist()
expect(res).to.be.eql([Buffer.from('hey!')])
done()
})
)
})
})

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

@ -0,0 +1,281 @@
/* 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 os = require('os')
const path = require('path')
const multiaddr = require('multiaddr')
const pipe = require('it-pipe')
const { collect, map } = require('streaming-iterables')
const isCI = process.env.CI
const isWindows = os.platform() === 'win32'
const skipOnWindows = isWindows ? it.skip : it
describe('construction', () => {
it('requires an upgrader', () => {
expect(() => new TCP()).to.throw()
})
})
describe('listen', () => {
let tcp
let listener
beforeEach(() => {
tcp = new TCP({
upgrader: {
upgradeOutbound: maConn => maConn,
upgradeInbound: maConn => maConn
}
})
})
afterEach(async () => {
listener && await listener.close()
})
it('close listener with connections, through timeout', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
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()
})
})
})
// Windows doesn't support unix paths
skipOnWindows('listen on path', async () => {
const mh = multiaddr(`/unix${path.resolve(os.tmpdir(), '/tmp/p2pd.sock')}`)
listener = tcp.createListener((conn) => {})
await listener.listen(mh)
})
it('listen on port 0', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/0')
listener = tcp.createListener((conn) => {})
await listener.listen(mh)
})
it('listen on IPv6 addr', async () => {
if (isCI) {
return
}
const mh = multiaddr('/ip6/::/tcp/9090')
listener = tcp.createListener((conn) => {})
await listener.listen(mh)
})
it('listen on any Interface', async () => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090')
listener = tcp.createListener((conn) => {})
await listener.listen(mh)
})
it('getAddrs', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090')
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)
})
it('getAddrs on port 0 listen', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/0')
listener = tcp.createListener((conn) => {})
await listener.listen(mh)
const multiaddrs = listener.getAddrs()
expect(multiaddrs.length).to.equal(1)
})
it('getAddrs from listening on 0.0.0.0', async () => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090')
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)
})
it('getAddrs from listening on 0.0.0.0 and port 0', async () => {
const mh = multiaddr('/ip4/0.0.0.0/tcp/0')
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)
})
it('getAddrs preserves IPFS Id', async () => {
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
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)
})
})
describe('dial', () => {
let tcp
let listener
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090')
beforeEach(async () => {
tcp = new TCP({
upgrader: {
upgradeOutbound: maConn => maConn,
upgradeInbound: maConn => maConn
}
})
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()
})
// Windows doesn't support unix paths
skipOnWindows('dial on path', async () => {
const ma = multiaddr(`/unix${path.resolve(os.tmpdir(), '/tmp/p2pd.sock')}`)
const listener = tcp.createListener((conn) => {
pipe(conn, conn)
})
await listener.listen(ma)
const connection = await tcp.dial(ma)
const values = await pipe(
['hey'],
connection,
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!')])
})
})

View File

@ -1,16 +0,0 @@
/* 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')
describe.skip('turbolence', () => {
it('dialer - emits error on the other end is terminated abruptly', (done) => {
expect('ok').to.equal('ok')
})
it('listener - emits error on the other end is terminated abruptly', (done) => {})
})