diff --git a/README.md b/README.md
index a8ccd7a..9efde84 100644
--- a/README.md
+++ b/README.md
@@ -1,44 +1,49 @@
-# @libp2p/http-v1-content-routing
+# @libp2p/delegated-routing-v1-http-api-content-routing
[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io)
-[![codecov](https://img.shields.io/codecov/c/github/libp2p/js-http-v1-content-routing.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-http-v1-content-routing)
-[![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-http-v1-content-routing/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/libp2p/js-http-v1-content-routing/actions/workflows/js-test-and-release.yml?query=branch%3Amain)
+[![codecov](https://img.shields.io/codecov/c/github/libp2p/js-delegated-routing-v1-http-api-content-routing.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-delegated-routing-v1-http-api-content-routing)
+[![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-delegated-routing-v1-http-api-content-routing/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/libp2p/js-delegated-routing-v1-http-api-content-routing/actions/workflows/js-test-and-release.yml?query=branch%3Amain)
-> Use a Routing V1 HTTP service to discover content providers
+> Use a Delegated Routing V1 HTTP service to discover content providers
+
+This is a [ContentRouting](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.content_routing.ContentRouting.html)
+implementation that makes use of the [@helia/delegated-routing-v1-http-api-client](https://www.npmjs.com/package/@helia/delegated-routing-v1-http-api-client)
+to use servers that implement the snappily-titled [Delegated Routing V1 HTTP API](Delegated Routing V1 HTTP API)
+spec to get/put IPNS records and to resolve providers for CIDs.
## Table of contents
-- [Install](#install)
- - [Browser `
+
```
-## Example
+# Example
```js
import { createLibp2p } from 'libp2p'
-import { reframeContentRouting } from '@libp2p/reframe-content-routing'
+import { delgatedRoutingV1HTTPAPIContentRouting } from '@libp2p/delegated-routing-http-v1-content-routing'
const node = await createLibp2p({
contentRouters: [
- reframeContentRouting('https://cid.contact/reframe')
+ delgatedRoutingV1HTTPAPIContentRouting('https://example.org')
]
//.. other config
})
@@ -51,7 +56,7 @@ for await (const provider of node.contentRouting.findProviders('cid')) {
## API Docs
--
+-
## License
diff --git a/package.json b/package.json
index 6796171..7a583c6 100644
--- a/package.json
+++ b/package.json
@@ -1,15 +1,15 @@
{
- "name": "@libp2p/http-v1-content-routing",
- "version": "1.0.2",
- "description": "Use a Routing V1 HTTP service to discover content providers",
+ "name": "@libp2p/delegated-routing-v1-http-api-content-routing",
+ "version": "0.0.0",
+ "description": "Use a Delegated Routing V1 HTTP service to discover content providers",
"license": "Apache-2.0 OR MIT",
- "homepage": "https://github.com/libp2p/js-http-v1-content-routing#readme",
+ "homepage": "https://github.com/libp2p/js-delegated-routing-v1-http-api-content-routing#readme",
"repository": {
"type": "git",
- "url": "git+https://github.com/libp2p/js-http-v1-content-routing.git"
+ "url": "git+https://github.com/libp2p/js-delegated-routing-v1-http-api-content-routing.git"
},
"bugs": {
- "url": "https://github.com/libp2p/js-http-v1-content-routing/issues"
+ "url": "https://github.com/libp2p/js-delegated-routing-v1-http-api-content-routing/issues"
},
"type": "module",
"types": "./dist/src/index.d.ts",
@@ -130,20 +130,17 @@
"test:node": "aegir test -t node --cov",
"test:electron-main": "aegir test -t electron-main",
"release": "aegir release",
- "docs": "aegir docs"
+ "docs": "aegir docs -- --includeVersion false"
},
"dependencies": {
+ "@helia/delegated-routing-v1-http-api-client": "^1.0.1",
"@libp2p/interface": "^0.1.1",
"@libp2p/logger": "^3.0.1",
"@libp2p/peer-id": "^3.0.1",
- "@multiformats/multiaddr": "^12.1.2",
- "any-signal": "^4.1.1",
- "browser-readablestream-to-it": "^2.0.2",
- "it-to-buffer": "^4.0.1",
+ "ipns": "^7.0.1",
+ "it-map": "^3.0.4",
"multiformats": "^12.0.1",
- "p-defer": "^4.0.0",
- "p-queue": "^7.3.4",
- "uint8arrays": "^4.0.3"
+ "uint8arrays": "^4.0.6"
},
"devDependencies": {
"@libp2p/peer-id-factory": "^3.0.2",
diff --git a/src/index.ts b/src/index.ts
index 1b0fa89..38c5517 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,34 +1,31 @@
+import { type DelegatedRoutingV1HttpApiClient, createDelegatedRoutingV1HttpApiClient } from '@helia/delegated-routing-v1-http-api-client'
import { CodeError } from '@libp2p/interface/errors'
import { logger } from '@libp2p/logger'
-import { peerIdFromString } from '@libp2p/peer-id'
-import { multiaddr } from '@multiformats/multiaddr'
-import { anySignal } from 'any-signal'
-import toIt from 'browser-readablestream-to-it'
-import toBuffer from 'it-to-buffer'
-import defer from 'p-defer'
-import PQueue from 'p-queue'
-import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
+import { peerIdFromBytes } from '@libp2p/peer-id'
+import { marshal, unmarshal } from 'ipns'
+import map from 'it-map'
+import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
+import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import type { AbortOptions } from '@libp2p/interface'
import type { ContentRouting } from '@libp2p/interface/content-routing'
+import type { PeerId } from '@libp2p/interface/peer-id'
import type { PeerInfo } from '@libp2p/interface/peer-info'
import type { Startable } from '@libp2p/interface/startable'
-import type { Multiaddr } from '@multiformats/multiaddr'
import type { CID } from 'multiformats/cid'
-const log = logger('reframe-content-routing')
+const log = logger('delegated-routing-v1-http-api-content-routing')
-export interface ReframeV1Response {
- Providers: ReframeV1ResponseItem[]
+const IPNS_PREFIX = uint8ArrayFromString('/ipns/')
+
+function isIPNSKey (key: Uint8Array): boolean {
+ return uint8ArrayEquals(key.subarray(0, IPNS_PREFIX.byteLength), IPNS_PREFIX)
}
-export interface ReframeV1ResponseItem {
- ID: string
- Addrs: string[]
- Protocol: string
- Schema: string
+const peerIdFromRoutingKey = (key: Uint8Array): PeerId => {
+ return peerIdFromBytes(key.slice(IPNS_PREFIX.length))
}
-export interface ReframeContentRoutingInit {
+export interface DelegatedRoutingV1HTTPAPIContentRoutingInit {
/**
* A concurrency limit to avoid request flood in web browser (default: 4)
*
@@ -42,33 +39,21 @@ export interface ReframeContentRoutingInit {
timeout?: number
}
-const defaultValues = {
- concurrentRequests: 4,
- timeout: 30e3
-}
-
/**
* An implementation of content routing, using a delegated peer
*/
-class ReframeContentRouting implements ContentRouting, Startable {
+class DelegatedRoutingV1HTTPAPIContentRouting implements ContentRouting, Startable {
private started: boolean
- private readonly httpQueue: PQueue
- private readonly shutDownController: AbortController
- private readonly clientUrl: URL
- private readonly timeout: number
+ private readonly client: DelegatedRoutingV1HttpApiClient
/**
* Create a new DelegatedContentRouting instance
*/
- constructor (url: string | URL, init: ReframeContentRoutingInit = {}) {
+ constructor (url: string | URL, init: DelegatedRoutingV1HTTPAPIContentRoutingInit = {}) {
this.started = false
- this.shutDownController = new AbortController()
- this.httpQueue = new PQueue({
- concurrency: init.concurrentRequests ?? defaultValues.concurrentRequests
- })
- this.clientUrl = url instanceof URL ? url : new URL(url)
- this.timeout = init.timeout ?? defaultValues.timeout
- log('enabled Reframe routing via', url)
+ this.client = createDelegatedRoutingV1HttpApiClient(new URL(url), init)
+
+ log('enabled Delegated Routing V1 HTTP API Content Routing via', url)
}
isStarted (): boolean {
@@ -80,85 +65,47 @@ class ReframeContentRouting implements ContentRouting, Startable {
}
stop (): void {
- this.httpQueue.clear()
- this.shutDownController.abort()
+ this.client.stop()
this.started = false
}
- async * findProviders (key: CID, options: AbortOptions = {}): AsyncIterable {
- log('findProviders starts: %c', key)
-
- const signal = anySignal([this.shutDownController.signal, options.signal, AbortSignal.timeout(this.timeout)])
- const onStart = defer()
- const onFinish = defer()
-
- void this.httpQueue.add(async () => {
- onStart.resolve()
- return onFinish.promise
- })
-
- try {
- await onStart.promise
-
- // https://github.com/ipfs/specs/blob/main/routing/ROUTING_V1_HTTP.md#api
- const resource = `${this.clientUrl}routing/v1/providers/${key.toString()}`
- const getOptions = { headers: { Accept: 'application/x-ndjson' }, signal }
- const a = await fetch(resource, getOptions)
-
- if (a.body == null) {
- throw new CodeError('Reframe response had no body', 'ERR_BAD_RESPONSE')
+ async * findProviders (cid: CID, options: AbortOptions = {}): AsyncIterable {
+ yield * map(this.client.getProviders(cid, options), (record) => {
+ return {
+ id: record.ID,
+ multiaddrs: record.Addrs ?? [],
+ protocols: []
}
-
- const body = await toBuffer(toIt(a.body))
- const result: ReframeV1Response = JSON.parse(uint8ArrayToString(body))
-
- for await (const event of result.Providers) {
- if (event.Protocol !== 'transport-bitswap' || event.Schema !== 'bitswap') {
- continue
- }
-
- yield this.mapEvent(event)
- }
- } catch (err) {
- log.error('findProviders errored:', err)
- } finally {
- signal.clear()
- onFinish.resolve()
- log('findProviders finished: %c', key)
- }
+ })
}
- private mapEvent (event: ReframeV1ResponseItem): PeerInfo {
- const peer = peerIdFromString(event.ID)
- const ma: Multiaddr[] = []
+ async provide (): Promise {
+ // noop
+ }
- for (const strAddr of event.Addrs) {
- const addr = multiaddr(strAddr)
- ma.push(addr)
+ async put (key: Uint8Array, value: Uint8Array, options?: AbortOptions): Promise {
+ if (!isIPNSKey(key)) {
+ return
}
- const pi = {
- id: peer,
- multiaddrs: ma,
- protocols: []
- }
+ const peerId = peerIdFromRoutingKey(key)
+ const record = unmarshal(value)
- return pi
+ await this.client.putIPNS(peerId, record, options)
}
- async provide (): Promise {
- // noop
- }
+ async get (key: Uint8Array, options?: AbortOptions): Promise {
+ if (!isIPNSKey(key)) {
+ throw new CodeError('Not found', 'ERR_NOT_FOUND')
+ }
- async put (): Promise {
- // noop
- }
+ const peerId = peerIdFromRoutingKey(key)
+ const record = await this.client.getIPNS(peerId, options)
- async get (): Promise {
- throw new CodeError('Not found', 'ERR_NOT_FOUND')
+ return marshal(record)
}
}
-export function reframeContentRouting (url: string | URL, init: ReframeContentRoutingInit = {}): () => ContentRouting {
- return () => new ReframeContentRouting(url, init)
+export function delgatedRoutingV1HTTPAPIContentRouting (url: string | URL, init: DelegatedRoutingV1HTTPAPIContentRoutingInit = {}): () => ContentRouting {
+ return () => new DelegatedRoutingV1HTTPAPIContentRouting(url, init)
}
diff --git a/test/index.spec.ts b/test/index.spec.ts
index 8a6237e..59ee6b7 100644
--- a/test/index.spec.ts
+++ b/test/index.spec.ts
@@ -4,7 +4,7 @@ import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import { expect } from 'aegir/chai'
import all from 'it-all'
import { CID } from 'multiformats/cid'
-import { type ReframeV1ResponseItem, reframeContentRouting } from '../src/index.js'
+import { delgatedRoutingV1HTTPAPIContentRouting } from '../src/index.js'
if (process.env.ECHO_SERVER == null) {
throw new Error('Echo server not configured correctly')
@@ -15,9 +15,9 @@ const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn')
describe('ReframeContentRouting', function () {
it('should find providers', async () => {
- const providers: ReframeV1ResponseItem[] = [{
- Protocol: 'transport-bitswap',
- Schema: 'bitswap',
+ const providers = [{
+ Protocols: ['transport-bitswap'],
+ Schema: 'peer',
ID: (await createEd25519PeerId()).toString(),
Addrs: ['/ip4/41.41.41.41/tcp/1234']
}, {
@@ -25,17 +25,19 @@ describe('ReframeContentRouting', function () {
Schema: 'bitswap',
ID: (await createEd25519PeerId()).toString(),
Addrs: ['/ip4/42.42.42.42/tcp/1234']
+ }, {
+ Schema: 'unknown',
+ ID: (await createEd25519PeerId()).toString(),
+ Addrs: ['/ip4/42.42.42.42/tcp/1234']
}]
// load providers for the router to fetch
await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, {
method: 'POST',
- body: JSON.stringify({
- Providers: providers
- })
+ body: providers.map(prov => JSON.stringify(prov)).join('\n')
})
- const routing = reframeContentRouting(serverUrl)()
+ const routing = delgatedRoutingV1HTTPAPIContentRouting(serverUrl)()
const provs = await all(routing.findProviders(cid))
expect(provs.map(prov => ({
@@ -54,7 +56,7 @@ describe('ReframeContentRouting', function () {
body: 'not json'
})
- const routing = reframeContentRouting(serverUrl)()
+ const routing = delgatedRoutingV1HTTPAPIContentRouting(serverUrl)()
const provs = await all(routing.findProviders(cid))
expect(provs).to.be.empty()
@@ -84,14 +86,14 @@ describe('ReframeContentRouting', function () {
})
})
- const routing = reframeContentRouting(serverUrl)()
+ const routing = delgatedRoutingV1HTTPAPIContentRouting(serverUrl)()
const provs = await all(routing.findProviders(cid))
expect(provs).to.be.empty()
})
it('should handle empty input', async () => {
- const routing = reframeContentRouting(serverUrl)()
+ const routing = delgatedRoutingV1HTTPAPIContentRouting(serverUrl)()
const provs = await all(routing.findProviders(cid))
expect(provs).to.be.empty()