Skip to content

Commit 9f88c54

Browse files
authored
fix: update @helia/ipns and dns config (#18)
Updates dns config to allow specifying per-TLD dns resolvers without needing to add things to the second options object.
1 parent 4e6775c commit 9f88c54

10 files changed

+125
-43
lines changed

packages/verified-fetch/README.md

+24-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ Note that you do not need to provide both a DNS-over-HTTPS and a DNS-over-JSON r
177177

178178
```typescript
179179
import { createVerifiedFetch } from '@helia/verified-fetch'
180-
import { dnsJsonOverHttps, dnsOverHttps } from '@helia/ipns/dns-resolvers'
180+
import { dnsJsonOverHttps, dnsOverHttps } from '@multiformats/dns/resolvers'
181181

182182
const fetch = await createVerifiedFetch({
183183
gateways: ['https://trustless-gateway.link'],
@@ -189,6 +189,29 @@ const fetch = await createVerifiedFetch({
189189
})
190190
```
191191

192+
## Example - Customizing DNS per-TLD resolvers
193+
194+
DNS resolvers can be configured to only service DNS queries for specific
195+
TLDs:
196+
197+
```typescript
198+
import { createVerifiedFetch } from '@helia/verified-fetch'
199+
import { dnsJsonOverHttps, dnsOverHttps } from '@multiformats/dns/resolvers'
200+
201+
const fetch = await createVerifiedFetch({
202+
gateways: ['https://trustless-gateway.link'],
203+
routers: ['http://delegated-ipfs.dev'],
204+
dnsResolvers: {
205+
// this resolver will only be used for `.com` domains (note - this could
206+
// also be an array of resolvers)
207+
'com.': dnsJsonOverHttps('https://my-dns-resolver.example.com/dns-json'),
208+
// this resolver will be used for everything else (note - this could
209+
// also be an array of resolvers)
210+
'.': dnsOverHttps('https://my-dns-resolver.example.com/dns-query')
211+
}
212+
})
213+
```
214+
192215
### IPLD codec handling
193216

194217
IPFS supports several data formats (typically referred to as codecs) which are included in the CID. `@helia/verified-fetch` attempts to abstract away some of the details for easier consumption.

packages/verified-fetch/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"@helia/car": "^3.1.0",
6262
"@helia/http": "^1.0.2",
6363
"@helia/interface": "^4.0.1",
64-
"@helia/ipns": "^6.0.1",
64+
"@helia/ipns": "^7.0.0",
6565
"@helia/routers": "^1.0.1",
6666
"@helia/unixfs": "^3.0.1",
6767
"@ipld/dag-cbor": "^9.2.0",
@@ -70,6 +70,7 @@
7070
"@libp2p/interface": "^1.1.4",
7171
"@libp2p/kad-dht": "^12.0.8",
7272
"@libp2p/peer-id": "^4.0.7",
73+
"@multiformats/dns": "^1.0.2",
7374
"cborg": "^4.0.9",
7475
"hashlru": "^2.3.0",
7576
"interface-blockstore": "^5.2.10",
@@ -88,7 +89,7 @@
8889
"@helia/dag-cbor": "^3.0.1",
8990
"@helia/dag-json": "^3.0.1",
9091
"@helia/json": "^3.0.1",
91-
"@helia/utils": "^0.0.2",
92+
"@helia/utils": "^0.1.0",
9293
"@ipld/car": "^5.3.0",
9394
"@libp2p/interface-compliance-tests": "^5.3.2",
9495
"@libp2p/logger": "^4.0.7",

packages/verified-fetch/src/index.ts

+49-8
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@
148148
*
149149
* ```typescript
150150
* import { createVerifiedFetch } from '@helia/verified-fetch'
151-
* import { dnsJsonOverHttps, dnsOverHttps } from '@helia/ipns/dns-resolvers'
151+
* import { dnsJsonOverHttps, dnsOverHttps } from '@multiformats/dns/resolvers'
152152
*
153153
* const fetch = await createVerifiedFetch({
154154
* gateways: ['https://trustless-gateway.link'],
@@ -160,6 +160,29 @@
160160
* })
161161
* ```
162162
*
163+
* @example Customizing DNS per-TLD resolvers
164+
*
165+
* DNS resolvers can be configured to only service DNS queries for specific
166+
* TLDs:
167+
*
168+
* ```typescript
169+
* import { createVerifiedFetch } from '@helia/verified-fetch'
170+
* import { dnsJsonOverHttps, dnsOverHttps } from '@multiformats/dns/resolvers'
171+
*
172+
* const fetch = await createVerifiedFetch({
173+
* gateways: ['https://trustless-gateway.link'],
174+
* routers: ['http://delegated-ipfs.dev'],
175+
* dnsResolvers: {
176+
* // this resolver will only be used for `.com` domains (note - this could
177+
* // also be an array of resolvers)
178+
* 'com.': dnsJsonOverHttps('https://my-dns-resolver.example.com/dns-json'),
179+
* // this resolver will be used for everything else (note - this could
180+
* // also be an array of resolvers)
181+
* '.': dnsOverHttps('https://my-dns-resolver.example.com/dns-query')
182+
* }
183+
* })
184+
* ```
185+
*
163186
* ### IPLD codec handling
164187
*
165188
* IPFS supports several data formats (typically referred to as codecs) which are included in the CID. `@helia/verified-fetch` attempts to abstract away some of the details for easier consumption.
@@ -569,10 +592,13 @@
569592
import { trustlessGateway } from '@helia/block-brokers'
570593
import { createHeliaHTTP } from '@helia/http'
571594
import { delegatedHTTPRouting } from '@helia/routers'
595+
import { dns } from '@multiformats/dns'
572596
import { VerifiedFetch as VerifiedFetchClass } from './verified-fetch.js'
573597
import type { Helia } from '@helia/interface'
574-
import type { DNSResolver, IPNSRoutingEvents, ResolveDnsLinkProgressEvents, ResolveProgressEvents } from '@helia/ipns'
598+
import type { ResolveDNSLinkProgressEvents } from '@helia/ipns'
575599
import type { GetEvents } from '@helia/unixfs'
600+
import type { DNSResolvers, DNS } from '@multiformats/dns'
601+
import type { DNSResolver } from '@multiformats/dns/resolvers'
576602
import type { CID } from 'multiformats/cid'
577603
import type { ProgressEvent, ProgressOptions } from 'progress-events'
578604

@@ -618,7 +644,7 @@ export interface CreateVerifiedFetchInit {
618644
*
619645
* @default [dnsJsonOverHttps('https://mozilla.cloudflare-dns.com/dns-query'),dnsJsonOverHttps('https://dns.google/resolve')]
620646
*/
621-
dnsResolvers?: DNSResolver[]
647+
dnsResolvers?: DNSResolver[] | DNSResolvers
622648
}
623649

624650
export interface CreateVerifiedFetchOptions {
@@ -651,7 +677,7 @@ export type BubbledProgressEvents =
651677
// unixfs
652678
GetEvents |
653679
// ipns
654-
ResolveProgressEvents | ResolveDnsLinkProgressEvents | IPNSRoutingEvents
680+
ResolveDNSLinkProgressEvents
655681

656682
export type VerifiedFetchProgressEvents =
657683
ProgressEvent<'verified-fetch:request:start', CIDDetail> |
@@ -674,20 +700,19 @@ export interface VerifiedFetchInit extends RequestInit, ProgressOptions<BubbledP
674700
* Create and return a Helia node
675701
*/
676702
export async function createVerifiedFetch (init?: Helia | CreateVerifiedFetchInit, options?: CreateVerifiedFetchOptions): Promise<VerifiedFetch> {
677-
let dnsResolvers: DNSResolver[] | undefined
678703
if (!isHelia(init)) {
679-
dnsResolvers = init?.dnsResolvers
680704
init = await createHeliaHTTP({
681705
blockBrokers: [
682706
trustlessGateway({
683707
gateways: init?.gateways
684708
})
685709
],
686-
routers: (init?.routers ?? ['https://delegated-ipfs.dev']).map((routerUrl) => delegatedHTTPRouting(routerUrl))
710+
routers: (init?.routers ?? ['https://delegated-ipfs.dev']).map((routerUrl) => delegatedHTTPRouting(routerUrl)),
711+
dns: createDns(init?.dnsResolvers)
687712
})
688713
}
689714

690-
const verifiedFetchInstance = new VerifiedFetchClass({ helia: init }, { dnsResolvers, ...options })
715+
const verifiedFetchInstance = new VerifiedFetchClass({ helia: init }, options)
691716
async function verifiedFetch (resource: Resource, options?: VerifiedFetchInit): Promise<Response> {
692717
return verifiedFetchInstance.fetch(resource, options)
693718
}
@@ -707,3 +732,19 @@ function isHelia (obj: any): obj is Helia {
707732
obj?.stop != null &&
708733
obj?.start != null
709734
}
735+
736+
function createDns (resolvers?: DNSResolver[] | DNSResolvers): DNS | undefined {
737+
if (resolvers == null) {
738+
return
739+
}
740+
741+
if (Array.isArray(resolvers)) {
742+
return dns({
743+
resolvers: {
744+
'.': resolvers
745+
}
746+
})
747+
}
748+
749+
return dns({ resolvers })
750+
}

packages/verified-fetch/src/utils/parse-resource.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { CID } from 'multiformats/cid'
22
import { parseUrlString } from './parse-url-string.js'
33
import type { ParsedUrlStringResults } from './parse-url-string.js'
44
import type { Resource } from '../index.js'
5-
import type { IPNS, IPNSRoutingEvents, ResolveDnsLinkProgressEvents, ResolveProgressEvents } from '@helia/ipns'
5+
import type { IPNS, IPNSRoutingEvents, ResolveDNSLinkProgressEvents, ResolveProgressEvents } from '@helia/ipns'
66
import type { ComponentLogger } from '@libp2p/interface'
77
import type { ProgressOptions } from 'progress-events'
88

@@ -11,7 +11,7 @@ export interface ParseResourceComponents {
1111
logger: ComponentLogger
1212
}
1313

14-
export interface ParseResourceOptions extends ProgressOptions<ResolveProgressEvents | IPNSRoutingEvents | ResolveDnsLinkProgressEvents> {
14+
export interface ParseResourceOptions extends ProgressOptions<ResolveProgressEvents | IPNSRoutingEvents | ResolveDNSLinkProgressEvents> {
1515

1616
}
1717
/**

packages/verified-fetch/src/utils/parse-url-string.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { peerIdFromString } from '@libp2p/peer-id'
22
import { CID } from 'multiformats/cid'
33
import { TLRU } from './tlru.js'
44
import type { RequestFormatShorthand } from '../types.js'
5-
import type { IPNS, IPNSRoutingEvents, ResolveDnsLinkProgressEvents, ResolveProgressEvents, ResolveResult } from '@helia/ipns'
5+
import type { IPNS, ResolveDNSLinkProgressEvents, ResolveResult } from '@helia/ipns'
66
import type { ComponentLogger } from '@libp2p/interface'
77
import type { ProgressOptions } from 'progress-events'
88

@@ -13,7 +13,7 @@ export interface ParseUrlStringInput {
1313
ipns: IPNS
1414
logger: ComponentLogger
1515
}
16-
export interface ParseUrlStringOptions extends ProgressOptions<ResolveProgressEvents | IPNSRoutingEvents | ResolveDnsLinkProgressEvents> {
16+
export interface ParseUrlStringOptions extends ProgressOptions<ResolveDNSLinkProgressEvents> {
1717

1818
}
1919

@@ -134,7 +134,7 @@ export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStrin
134134
log.trace('Attempting to resolve DNSLink for %s', decodedDnsLinkLabel)
135135

136136
try {
137-
resolveResult = await ipns.resolveDns(decodedDnsLinkLabel, { onProgress: options?.onProgress })
137+
resolveResult = await ipns.resolveDNSLink(decodedDnsLinkLabel, { onProgress: options?.onProgress })
138138
cid = resolveResult?.cid
139139
resolvedPath = resolveResult?.path
140140
log.trace('resolved %s to %c', decodedDnsLinkLabel, cid)

packages/verified-fetch/src/verified-fetch.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { car } from '@helia/car'
2-
import { ipns as heliaIpns, type DNSResolver, type IPNS } from '@helia/ipns'
3-
import { dnsJsonOverHttps } from '@helia/ipns/dns-resolvers'
2+
import { ipns as heliaIpns, type IPNS } from '@helia/ipns'
43
import { unixfs as heliaUnixFs, type UnixFS as HeliaUnixFs, type UnixFSStats } from '@helia/unixfs'
54
import * as ipldDagCbor from '@ipld/dag-cbor'
65
import * as ipldDagJson from '@ipld/dag-json'
@@ -29,6 +28,7 @@ import type { CIDDetail, ContentTypeParser, Resource, VerifiedFetchInit as Verif
2928
import type { RequestFormatShorthand } from './types.js'
3029
import type { Helia } from '@helia/interface'
3130
import type { AbortOptions, Logger, PeerId } from '@libp2p/interface'
31+
import type { DNSResolver } from '@multiformats/dns/resolvers'
3232
import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
3333
import type { CID } from 'multiformats/cid'
3434

@@ -126,12 +126,7 @@ export class VerifiedFetch {
126126
constructor ({ helia, ipns, unixfs }: VerifiedFetchComponents, init?: VerifiedFetchInit) {
127127
this.helia = helia
128128
this.log = helia.logger.forComponent('helia:verified-fetch')
129-
this.ipns = ipns ?? heliaIpns(helia, {
130-
resolvers: init?.dnsResolvers ?? [
131-
dnsJsonOverHttps('https://mozilla.cloudflare-dns.com/dns-query'),
132-
dnsJsonOverHttps('https://dns.google/resolve')
133-
]
134-
})
129+
this.ipns = ipns ?? heliaIpns(helia)
135130
this.unixfs = unixfs ?? heliaUnixFs(helia)
136131
this.contentTypeParser = init?.contentTypeParser
137132
this.log.trace('created VerifiedFetch instance')

packages/verified-fetch/test/custom-dns-resolvers.spec.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { stop } from '@libp2p/interface'
2+
import { dns, RecordType } from '@multiformats/dns'
23
import { expect } from 'aegir/chai'
34
import Sinon from 'sinon'
45
import { createVerifiedFetch } from '../src/index.js'
@@ -30,23 +31,42 @@ describe('custom dns-resolvers', () => {
3031
await expect(fetch('ipns://some-non-cached-domain.com')).to.eventually.be.rejected.with.property('errors')
3132

3233
expect(customDnsResolver.callCount).to.equal(1)
33-
expect(customDnsResolver.getCall(0).args).to.deep.equal(['some-non-cached-domain.com', { onProgress: undefined }])
34+
expect(customDnsResolver.getCall(0).args).to.deep.equal(['_dnslink.some-non-cached-domain.com', {
35+
onProgress: undefined,
36+
types: [
37+
RecordType.TXT
38+
]
39+
}])
3440
})
3541

3642
it('is used when passed to VerifiedFetch', async () => {
37-
const customDnsResolver = Sinon.stub()
43+
const customDnsResolver = Sinon.stub().withArgs('_dnslink.some-non-cached-domain2.com').resolves({
44+
Answer: [{
45+
data: 'dnslink=/ipfs/QmVP2ip92jQuMDezVSzQBWDqWFbp9nyCHNQSiciRauPLDg'
46+
}]
47+
})
3848

39-
customDnsResolver.returns(Promise.resolve('/ipfs/QmVP2ip92jQuMDezVSzQBWDqWFbp9nyCHNQSiciRauPLDg'))
49+
await stop(helia)
50+
helia = await createHelia({
51+
dns: dns({
52+
resolvers: {
53+
'.': customDnsResolver
54+
}
55+
})
56+
})
4057

4158
const verifiedFetch = new VerifiedFetch({
4259
helia
43-
}, {
44-
dnsResolvers: [customDnsResolver]
4560
})
4661
// error of walking the CID/dag because we haven't actually added the block to the blockstore
4762
await expect(verifiedFetch.fetch('ipns://some-non-cached-domain2.com')).to.eventually.be.rejected.with.property('errors').that.has.lengthOf(0)
4863

4964
expect(customDnsResolver.callCount).to.equal(1)
50-
expect(customDnsResolver.getCall(0).args).to.deep.equal(['some-non-cached-domain2.com', { onProgress: undefined }])
65+
expect(customDnsResolver.getCall(0).args).to.deep.equal(['_dnslink.some-non-cached-domain2.com', {
66+
onProgress: undefined,
67+
types: [
68+
RecordType.TXT
69+
]
70+
}])
5171
})
5272
})

packages/verified-fetch/test/fixtures/create-offline-helia.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import { Helia as HeliaClass } from '@helia/utils'
22
import { MemoryBlockstore } from 'blockstore-core'
33
import { MemoryDatastore } from 'datastore-core'
4+
import type { HeliaHTTPInit } from '@helia/http'
45
import type { Helia } from '@helia/interface'
56

6-
export async function createHelia (): Promise<Helia> {
7+
export async function createHelia (init: Partial<HeliaHTTPInit> = {}): Promise<Helia> {
78
const datastore = new MemoryDatastore()
89
const blockstore = new MemoryBlockstore()
910

1011
const helia = new HeliaClass({
1112
datastore,
1213
blockstore,
1314
blockBrokers: [],
14-
routers: []
15+
routers: [],
16+
...init
1517
})
1618

1719
await helia.start()

packages/verified-fetch/test/parse-resource.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('parseResource', () => {
1313
const shouldNotBeCalled2 = sinon.stub().throws(new Error('should not be called'))
1414
const { cid, path, query } = await parseResource(testCID, {
1515
ipns: stubInterface<IPNS>({
16-
resolveDns: shouldNotBeCalled1,
16+
resolveDNSLink: shouldNotBeCalled1,
1717
resolve: shouldNotBeCalled2
1818
}),
1919
logger: defaultLogger()

0 commit comments

Comments
 (0)