Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions packages/client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
const defaultValues = {
concurrentRequests: 4,
timeout: 30e3,
cacheTTL: 5 * 60 * 1000 // 5 minutes default as per https://specs.ipfs.tech/routing/http-routing-v1/#response-headers
cacheTTL: 5 * 60 * 1000, // 5 minutes default as per https://specs.ipfs.tech/routing/http-routing-v1/#response-headers
cacheName: 'delegated-routing-v1-cache'
}

export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV1HttpApiClient {
Expand All @@ -35,6 +36,7 @@
private readonly filterAddrs?: string[]
private readonly filterProtocols?: string[]
private readonly inFlightRequests: Map<string, Promise<Response>>
private readonly cacheName: string
private cache?: Cache
private readonly cacheTTL: number
/**
Expand All @@ -55,17 +57,8 @@
this.contentRouting = new DelegatedRoutingV1HttpApiClientContentRouting(this)
this.peerRouting = new DelegatedRoutingV1HttpApiClientPeerRouting(this)

this.cacheName = init.cacheName ?? defaultValues.cacheName
this.cacheTTL = init.cacheTTL ?? defaultValues.cacheTTL
const cacheEnabled = (typeof globalThis.caches !== 'undefined') && (this.cacheTTL > 0)

if (cacheEnabled) {
log('cache enabled with ttl %d', this.cacheTTL)
globalThis.caches.open('delegated-routing-v1-cache').then(cache => {
this.cache = cache
}).catch(() => {
this.cache = undefined
})
}
}

get [contentRoutingSymbol] (): ContentRouting {
Expand All @@ -80,19 +73,30 @@
return this.started
}

start (): void {
async start (): Promise<void> {
if (this.started) {
return
}

Check warning on line 79 in packages/client/src/client.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/client.ts#L78-L79

Added lines #L78 - L79 were not covered by tests

this.started = true

if (this.cacheTTL > 0) {
this.cache = await globalThis.caches?.open(this.cacheName)

if (this.cache != null) {
log('cache enabled with ttl %d', this.cacheTTL)
}
}
}

stop (): void {
async stop (): Promise<void> {
this.httpQueue.clear()
this.shutDownController.abort()
this.started = false

// Clear the cache when stopping
if (this.cache != null) {
void this.cache.delete('delegated-routing-v1-cache')
}
await globalThis.caches?.delete(this.cacheName)

this.started = false
}

async * getProviders (cid: CID, options: GetProvidersOptions = {}): AsyncGenerator<PeerRecord> {
Expand Down
17 changes: 16 additions & 1 deletion packages/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,15 @@ export interface DelegatedRoutingV1HttpApiClientInit extends FilterOptions {
* If 0, caching is disabled
*/
cacheTTL?: number

/**
* Where a [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) is
* available in the global scope, we will store request/responses to avoid
* making duplicate requests.
*
* @default 'delegated-routing-v1-cache'
*/
cacheName?: string
}

export interface GetIPNSOptions extends AbortOptions {
Expand Down Expand Up @@ -182,11 +191,17 @@ export interface DelegatedRoutingV1HttpApiClient {
*/
putIPNS(libp2pKey: CID<unknown, 0x72, 0x00 | 0x12, 1>, record: IPNSRecord, options?: AbortOptions): Promise<void>

/**
* Create the request/response cache used to ensure duplicate requests aren't
* made for the same data
*/
start(): Promise<void>

/**
* Shut down any currently running HTTP requests and clear up any resources
* that are in use
*/
stop(): void
stop(): Promise<void>
}

/**
Expand Down
17 changes: 17 additions & 0 deletions packages/client/test/client.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expect } from 'aegir/chai'
import { DefaultDelegatedRoutingV1HttpApiClient } from '../src/client.js'
import { itBrowser } from './fixtures/it.js'

describe('client', () => {
itBrowser('should remove cache on stop', async function () {
const cacheName = 'test-cache'

const client = new DefaultDelegatedRoutingV1HttpApiClient('http://example.com', {
cacheName
})
await client.start()
await client.stop()

await expect(globalThis.caches.has(cacheName)).to.eventually.be.false('did not remove cache on stop')

Check warning on line 15 in packages/client/test/client.spec.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/test/client.spec.ts#L7-L15

Added lines #L7 - L15 were not covered by tests
})
})
5 changes: 5 additions & 0 deletions packages/client/test/fixtures/it.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* eslint-env mocha */

import { isBrowser } from 'wherearewe'

export const itBrowser = (isBrowser ? it : it.skip)
14 changes: 7 additions & 7 deletions packages/client/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
/* eslint-env mocha */

import { generateKeyPair } from '@libp2p/crypto/keys'
import { start, stop } from '@libp2p/interface'
import { peerIdFromPrivateKey, peerIdFromString } from '@libp2p/peer-id'
import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
import { createIPNSRecord, marshalIPNSRecord } from 'ipns'
import all from 'it-all'
import { CID } from 'multiformats/cid'
import { isBrowser } from 'wherearewe'
import { createDelegatedRoutingV1HttpApiClient, type DelegatedRoutingV1HttpApiClient } from '../src/index.js'
import { itBrowser } from './fixtures/it.js'

if (process.env.ECHO_SERVER == null) {
throw new Error('Echo server not configured correctly')
}

const serverUrl = process.env.ECHO_SERVER
const itBrowser = (isBrowser ? it : it.skip)

describe('delegated-routing-v1-http-api-client', () => {
let client: DelegatedRoutingV1HttpApiClient

beforeEach(() => {
beforeEach(async () => {
client = createDelegatedRoutingV1HttpApiClient(new URL(serverUrl), { cacheTTL: 0 })
await start(client)
})

afterEach(async () => {
if (client != null) {
client.stop()
}
await stop(client)
})

it('should find providers', async () => {
Expand Down Expand Up @@ -357,6 +356,7 @@
const clientWithShortTTL = createDelegatedRoutingV1HttpApiClient(new URL(serverUrl), {
cacheTTL: shortTTL
})
await start(clientWithShortTTL)

Check warning on line 359 in packages/client/test/index.spec.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/test/index.spec.ts#L359

Added line #L359 was not covered by tests

const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn')
const providers = [{
Expand Down Expand Up @@ -395,6 +395,6 @@
callCount = parseInt(await (await fetch(`${process.env.ECHO_SERVER}/get-call-count`)).text(), 10)
expect(callCount).to.equal(2) // Second server call after cache expired

clientWithShortTTL.stop()
await stop(clientWithShortTTL)

Check warning on line 398 in packages/client/test/index.spec.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/test/index.spec.ts#L398

Added line #L398 was not covered by tests
})
})
16 changes: 7 additions & 9 deletions packages/client/test/routings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-env mocha */

import { generateKeyPair } from '@libp2p/crypto/keys'
import { contentRoutingSymbol, peerRoutingSymbol } from '@libp2p/interface'
import { contentRoutingSymbol, peerRoutingSymbol, start, stop } from '@libp2p/interface'
import { peerIdFromPrivateKey } from '@libp2p/peer-id'
import { expect } from 'aegir/chai'
import { createIPNSRecord, marshalIPNSRecord, multihashToIPNSRoutingKey } from 'ipns'
Expand All @@ -22,14 +22,13 @@ if (serverUrl == null) {
describe('libp2p content-routing', () => {
let client: DelegatedRoutingV1HttpApiClient

beforeEach(() => {
beforeEach(async () => {
client = createDelegatedRoutingV1HttpApiClient(new URL(serverUrl), { cacheTTL: 0 })
await start(client)
})

afterEach(async () => {
if (client != null) {
client.stop()
}
await stop(client)

const res = await fetch(`${process.env.ECHO_SERVER}/reset-call-count`)
await res.text()
Expand Down Expand Up @@ -192,14 +191,13 @@ describe('libp2p content-routing', () => {
describe('libp2p peer-routing', () => {
let client: DelegatedRoutingV1HttpApiClient

beforeEach(() => {
beforeEach(async () => {
client = createDelegatedRoutingV1HttpApiClient(new URL(serverUrl))
await start(client)
})

afterEach(async () => {
if (client != null) {
client.stop()
}
await stop(client)
})

describe('peer routing', () => {
Expand Down
7 changes: 4 additions & 3 deletions packages/interop/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createDelegatedRoutingV1HttpApiClient } from '@helia/delegated-routing-
import { createDelegatedRoutingV1HttpApiServer } from '@helia/delegated-routing-v1-http-api-server'
import { ipns } from '@helia/ipns'
import { generateKeyPair } from '@libp2p/crypto/keys'
import { start, stop } from '@libp2p/interface'
import { expect } from 'aegir/chai'
import { createIPNSRecord } from 'ipns'
import first from 'it-first'
Expand Down Expand Up @@ -43,12 +44,12 @@ describe('delegated-routing-v1-http-api interop', () => {
await node.libp2p.dial(remote.libp2p.getMultiaddrs())
}
}

await start(client)
})

afterEach(async () => {
if (client != null) {
client.stop()
}
await stop(client)

if (server != null) {
await server.close()
Expand Down
Loading