Skip to content

Commit

Permalink
feat: allow cancelling in-flight multiaddr resolve (#257)
Browse files Browse the repository at this point in the history
Doing a multiaddr resolve involves a DNS request which can be very
slow, so allow aborting the request before it's resolved.
  • Loading branch information
achingbrain authored Jul 7, 2022
1 parent 1e51b2f commit ddd751b
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 62 deletions.
3 changes: 3 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ updates:
interval: daily
time: "10:00"
open-pull-requests-limit: 10
commit-message:
prefix: "deps"
prefix-development: "deps(dev)"
78 changes: 40 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
js-multiaddr
============

[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
[![Dependency Status](https://david-dm.org/multiformats/js-multiaddr.svg?style=flat-square)](https://david-dm.org/multiformats/js-multiaddr)
[![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/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
[![](https://img.shields.io/travis/multiformats/js-multiaddr.svg?style=flat-square)](https://travis-ci.com/multiformats/js-multiaddr)
# @multiformats/multiaddr <!-- omit in toc -->

[![multiformats.io](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://multiformats.io)
[![codecov](https://img.shields.io/codecov/c/github/multiformats/js-multiaddr.svg?style=flat-square)](https://codecov.io/gh/multiformats/js-multiaddr)
[![CI](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-interfaces/test%20&%20maybe%20release/master?style=flat-square)](https://github.com/multiformats/js-multiaddr/actions/workflows/js-test-and-release.yml)

> multiaddr implementation (binary + string representation of network addresses)
## Table of contents <!-- omit in toc -->

- [Install](#install)
- [Lead Maintainer](#lead-maintainer)
- [Background](#background)
- [What is multiaddr?](#what-is-multiaddr)
- [Browser: `<script>` Tag](#browser-script-tag)
- [Usage](#usage)
- [API](#api)
- [Resolvers](#resolvers)
- [Contribute](#contribute)
- [License](#license)
- [Contribution](#contribution)

> JavaScript implementation of [multiaddr](https://github.com/multiformats/multiaddr).
## Install

```console
$ npm i @multiformats/multiaddr
```

## Lead Maintainer

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

## Table of Contents

- [js-multiaddr](#js-multiaddr)
- [Lead Maintainer](#lead-maintainer)
- [Table of Contents](#table-of-contents)
- [Background](#background)
- [What is multiaddr?](#what-is-multiaddr)
- [Install](#install)
- [NPM](#npm)
- [Browser: `<script>` Tag](#browser-script-tag)
- [Usage](#usage)
- [API](#api)
- [Resolvers](#resolvers)
- [Contribute](#contribute)
- [License](#license)

## Background

### What is multiaddr?
Expand All @@ -44,9 +42,6 @@ A standard way to represent addresses that
- have a nice string representation
- encapsulate well

## Install

### NPM
```sh
npm i multiaddr
```
Expand All @@ -64,12 +59,12 @@ the global namespace.

```js
// if we are coming from <= 8.x you can use the factory function
const { multiaddr } = require('multiaddr')
const addr =  multiaddr("/ip4/127.0.0.1/udp/1234")
const { multiaddr } = require('multiaddr')
const addr = multiaddr("/ip4/127.0.0.1/udp/1234")
// <Multiaddr /ip4/127.0.0.1/udp/1234>

// or just the class directly
const { Multiaddr } = require('multiaddr')
const { Multiaddr } = require('multiaddr')

const addr = new Multiaddr("/ip4/127.0.0.1/udp/1234")
// <Multiaddr /ip4/127.0.0.1/udp/1234>
Expand Down Expand Up @@ -104,11 +99,11 @@ addr.encapsulate('/sctp/5678')

## API

https://multiformats.github.io/js-multiaddr/
<https://multiformats.github.io/js-multiaddr/>

## Resolvers

`multiaddr` allows multiaddrs to be resolved when appropriate resolvers are provided. This module already has resolvers available, but you can also create your own. Resolvers should always be set in the same module that is calling `multiaddr.resolve()` to avoid conflicts if multiple versions of `multiaddr` are in your dependency tree.
`multiaddr` allows multiaddrs to be resolved when appropriate resolvers are provided. This module already has resolvers available, but you can also create your own. Resolvers should always be set in the same module that is calling `multiaddr.resolve()` to avoid conflicts if multiple versions of `multiaddr` are in your dependency tree.
To provide multiaddr resolvers you can do:

```js
Expand All @@ -120,8 +115,8 @@ Multiaddr.resolvers.set('dnsaddr', resolvers.dnsaddrResolver)

The available resolvers are:

| Name | type | Description |
|-------------|------|-------------|
| Name | type | Description |
| ----------------- | --------- | ----------------------------------- |
| `dnsaddrResolver` | `dnsaddr` | dnsaddr resolution with TXT Records |

A resolver receives a `Multiaddr` as a parameter and returns a `Promise<Array<string>>`.
Expand All @@ -136,4 +131,11 @@ Small note: If editing the README, please conform to the [standard-readme](https

## License

[MIT](LICENSE) © 2016 Protocol Labs Inc.
Licensed under either of

- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT ([LICENSE-MIT](LICENSE-MIT) / <http://opensource.org/licenses/MIT>)

## Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
34 changes: 20 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,15 @@
],
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"import": "./dist/src/index.js"
},
"./convert": {
"types": "./dist/src/convert.d.ts",
"import": "./dist/src/convert.js"
},
"./resolvers": {
"types": "./dist/src/resolvers/index.d.ts",
"import": "./dist/src/resolvers/index.js"
}
},
Expand Down Expand Up @@ -126,7 +129,11 @@
},
{
"type": "docs",
"section": "Trivial Changes"
"section": "Documentation"
},
{
"type": "deps",
"section": "Dependencies"
},
{
"type": "test",
Expand All @@ -144,20 +151,19 @@
},
"scripts": {
"lint": "aegir lint",
"dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js",
"build": "tsc",
"pretest": "npm run build",
"test": "aegir test -f ./dist/test/**/*.js",
"test:chrome": "npm run test -- -t browser",
"test:chrome-webworker": "npm run test -- -t webworker",
"test:firefox": "npm run test -- -t browser -- --browser firefox",
"test:firefox-webworker": "npm run test -- -t webworker -- --browser firefox",
"test:node": "npm run test -- -t node --cov",
"test:electron-main": "npm run test -- -t electron-main",
"release": "semantic-release"
"dep-check": "aegir dep-check",
"build": "aegir build",
"test": "aegir test -f",
"test:chrome": "aegir test -t browser",
"test:chrome-webworker": "aegir test -t webworker",
"test:firefox": "aegir test -t browser -- --browser firefox",
"test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
"test:node": "aegir test -t node --cov",
"test:electron-main": "aegir test -t electron-main",
"release": "aegir release"
},
"dependencies": {
"dns-over-http-resolver": "^2.0.1",
"dns-over-http-resolver": "^2.1.0",
"err-code": "^3.0.1",
"is-ip": "^4.0.0",
"multiformats": "^9.4.5",
Expand All @@ -166,7 +172,7 @@
},
"devDependencies": {
"@types/varint": "^6.0.0",
"aegir": "^36.1.3",
"aegir": "^37.4.5",
"sinon": "^13.0.1",
"util": "^0.12.3"
},
Expand Down
10 changes: 7 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ export interface NodeAddress {

export type MultiaddrInput = string | Multiaddr | Uint8Array | null

export interface Resolver { (addr: Multiaddr): Promise<string[]> }
export interface Resolver { (addr: Multiaddr, options?: AbortOptions): Promise<string[]> }

export interface AbortOptions {
signal?: AbortSignal
}

const resolvers = new Map<string, Resolver>()
const symbol = Symbol.for('@multiformats/js-multiaddr/multiaddr')
Expand Down Expand Up @@ -421,7 +425,7 @@ export class Multiaddr {
* // ]
* ```
*/
async resolve () {
async resolve (options?: AbortOptions) {
const resolvableProto = this.protos().find((p) => p.resolvable)

// Multiaddr is not resolvable?
Expand All @@ -434,7 +438,7 @@ export class Multiaddr {
throw errCode(new Error(`no available resolver for ${resolvableProto.name}`), 'ERR_NO_AVAILABLE_RESOLVER')
}

const addresses = await resolver(this)
const addresses = await resolver(this, options)
return addresses.map((a) => new Multiaddr(a))
}

Expand Down
11 changes: 9 additions & 2 deletions src/resolvers/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { getProtocol } from '../protocols-table.js'
import Resolver from './dns.js'
import type { Multiaddr } from '../index.js'
import type { AbortOptions, Multiaddr } from '../index.js'

const { code: dnsaddrCode } = getProtocol('dnsaddr')

/**
* Resolver for dnsaddr addresses.
*/
export async function dnsaddrResolver (addr: Multiaddr) {
export async function dnsaddrResolver (addr: Multiaddr, options: AbortOptions = {}) {
const resolver = new Resolver()

if (options.signal != null) {
options.signal.addEventListener('abort', () => {
resolver.cancel()
})
}

const peerId = addr.getPeerId()
const [, hostname] = addr.stringTuples().find(([proto]) => proto === dnsaddrCode) ?? []

Expand All @@ -18,6 +24,7 @@ export async function dnsaddrResolver (addr: Multiaddr) {
}

const records = await resolver.resolveTxt(`_dnsaddr.${hostname}`)

let addresses = records.flat().map((a) => a.split('=')[1])

if (peerId != null) {
Expand Down
2 changes: 1 addition & 1 deletion test/codec.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-env mocha */
import * as codec from '../src/codec.js'
import varint from 'varint'
import { expect } from 'aegir/utils/chai.js'
import { expect } from 'aegir/chai'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'

describe('codec', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/convert.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-env mocha */
import * as convert from '../src/convert.js'
import { expect } from 'aegir/utils/chai.js'
import { expect } from 'aegir/chai'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'

describe('convert', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-env mocha */
import { Multiaddr } from '../src/index.js'
import { expect } from 'aegir/utils/chai.js'
import { expect } from 'aegir/chai'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { codes } from '../src/protocols-table.js'

Expand Down
2 changes: 1 addition & 1 deletion test/protocols.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-env mocha */
import { getProtocol } from '../src/protocols-table.js'
import { expect } from 'aegir/utils/chai.js'
import { expect } from 'aegir/chai'

describe('protocols', () => {
describe('throws on non existent protocol', () => {
Expand Down
16 changes: 15 additions & 1 deletion test/resolvers.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-env mocha */
import { expect } from 'aegir/utils/chai.js'
import { expect } from 'aegir/chai'
import sinon from 'sinon'
import { Multiaddr } from '../src/index.js'
import * as resolvers from '../src/resolvers/index.js'
Expand Down Expand Up @@ -97,5 +97,19 @@ describe('multiaddr resolve', () => {
expect(ma.equals(new Multiaddr(stubAddr))).to.equal(true)
})
})

it('can cancel resolving', async () => {
const ma = new Multiaddr('/dnsaddr/bootstrap.libp2p.ii/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nc')
const controller = new AbortController()

// Resolve
const resolvePromise = ma.resolve({
signal: controller.signal
})

controller.abort()

await expect(resolvePromise).to.eventually.be.rejected().with.property('code', 'ECANCELLED')
})
})
})

0 comments on commit ddd751b

Please sign in to comment.