Skip to content

Commit 9344b6f

Browse files
committed
feat: preload DNSLink websites in background
With #826 data was no longer getting into the cache of local node. This adds background queue that ensures every visited DNSLink asset is preloaded to the cache of local IPFS node.
1 parent 3c9e72d commit 9344b6f

File tree

6 files changed

+65
-24
lines changed

6 files changed

+65
-24
lines changed

add-on/_locales/en/messages.json

+8
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,14 @@
287287
"message": "If global redirect is enabled, this will include DNSLink websites and redirect them to respective /ipns/{fqdn} paths at Custom Gateway",
288288
"description": "An option description on the Preferences screen (option_dnslinkRedirect_description)"
289289
},
290+
"option_dnslinkDataPreload_title": {
291+
"message": "Preload visited pages",
292+
"description": "An option title on the Preferences screen (option_dnslinkDataPreload_title)"
293+
},
294+
"option_dnslinkDataPreload_description": {
295+
"message": "DNSLink websites will be preloaded to the local IPFS node to enable offline access and improve resiliency against network failures",
296+
"description": "An option description on the Preferences screen (option_dnslinkDataPreload_description)"
297+
},
290298
"option_dnslinkRedirect_warning": {
291299
"message": "Redirecting to a path-based gateway breaks Origin-based security isolation of DNSLink website! Please leave this disabled unless you are aware of (and ok with) related risks.",
292300
"description": "A warning on the Preferences screen, displayed when URL does not belong to Secure Context (option_customGatewayUrl_warning)"

add-on/src/lib/dnslink.js

+33-16
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ module.exports = function createDnslinkResolver (getState) {
1717
// DNSLink lookup result cache
1818
const cacheOptions = { max: 1000, maxAge: 1000 * 60 * 60 * 12 }
1919
const cache = new LRU(cacheOptions)
20-
// upper bound for concurrent background lookups done by preloadDnslink(url)
21-
const lookupQueue = new PQueue({ concurrency: 8 })
20+
// upper bound for concurrent background lookups done by resolve(url)
21+
const lookupQueue = new PQueue({ concurrency: 4 })
22+
// preload of DNSLink data
23+
const preloadUrlCache = new LRU(cacheOptions)
24+
const preloadQueue = new PQueue({ concurrency: 4 })
2225

2326
const dnslinkResolver = {
2427

@@ -89,20 +92,34 @@ module.exports = function createDnslinkResolver (getState) {
8992
return dnslink
9093
},
9194

92-
// does not return anything, runs async lookup in the background
93-
// and saves result into cache with an optional callback
94-
preloadDnslink (url, cb) {
95-
if (dnslinkResolver.canLookupURL(url)) {
96-
lookupQueue.add(async () => {
97-
const fqdn = new URL(url).hostname
98-
const result = dnslinkResolver.readAndCacheDnslink(fqdn)
99-
if (cb) {
100-
cb(result)
101-
}
102-
})
103-
} else if (cb) {
104-
cb(null)
105-
}
95+
// runs async lookup in a queue in the background and returns the record
96+
async resolve (url) {
97+
if (!dnslinkResolver.canLookupURL(url)) return
98+
const fqdn = new URL(url).hostname
99+
const cachedResult = dnslinkResolver.cachedDnslink(fqdn)
100+
if (cachedResult) return cachedResult
101+
return lookupQueue.add(() => {
102+
return dnslinkResolver.readAndCacheDnslink(fqdn)
103+
})
104+
},
105+
106+
// preloads data behind the url to local node
107+
async preloadData (url) {
108+
const state = getState()
109+
if (!state.dnslinkDataPreload || state.dnslinkRedirect) return
110+
if (preloadUrlCache.get(url)) return
111+
preloadUrlCache.set(url, true)
112+
const dnslink = await dnslinkResolver.resolve(url)
113+
if (!dnslink) return
114+
if (state.ipfsNodeType === 'embedded') return
115+
if (state.peerCount < 1) return
116+
return preloadQueue.add(async () => {
117+
const { pathname } = new URL(url)
118+
const preloadUrl = new URL(state.gwURLString)
119+
preloadUrl.pathname = `${dnslink}${pathname}`
120+
await fetch(preloadUrl.toString(), { method: 'HEAD' })
121+
return preloadUrl
122+
})
106123
},
107124

108125
// low level lookup without cache

add-on/src/lib/ipfs-request.js

+11-8
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
8989
if (request.type === 'main_frame') {
9090
// lazily trigger DNSLink lookup (will do anything only if status for root domain is not in cache)
9191
if (state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) {
92-
dnslinkResolver.preloadDnslink(request.url)
92+
dnslinkResolver.resolve(request.url) // no await: preload record in background
9393
}
9494
}
9595
return isIgnored(request.requestId)
@@ -142,15 +142,18 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
142142
return redirectToGateway(request.url, state, ipfsPathValidator)
143143
}
144144
// Detect dnslink using heuristics enabled in Preferences
145-
if (state.dnslinkRedirect && state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) {
146-
const dnslinkRedirect = dnslinkResolver.dnslinkRedirect(request.url)
147-
if (dnslinkRedirect && isSafeToRedirect(request, runtime)) {
148-
// console.log('onBeforeRequest.dnslinkRedirect', dnslinkRedirect)
149-
return dnslinkRedirect
145+
if (state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) {
146+
if (state.dnslinkRedirect) {
147+
const dnslinkRedirect = dnslinkResolver.dnslinkRedirect(request.url)
148+
if (dnslinkRedirect && isSafeToRedirect(request, runtime)) {
149+
// console.log('onBeforeRequest.dnslinkRedirect', dnslinkRedirect)
150+
return dnslinkRedirect
151+
}
152+
} else if (state.dnslinkDataPreload) {
153+
dnslinkResolver.preloadData(request.url)
150154
}
151155
if (state.dnslinkPolicy === 'best-effort') {
152-
// dnslinkResolver.preloadDnslink(request.url, (dnslink) => console.log(`---> preloadDnslink(${new URL(request.url).hostname})=${dnslink}`))
153-
dnslinkResolver.preloadDnslink(request.url)
156+
dnslinkResolver.resolve(request.url)
154157
}
155158
}
156159
}

add-on/src/lib/options.js

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ exports.optionDefaults = Object.freeze({
1717
automaticMode: true,
1818
linkify: false,
1919
dnslinkPolicy: 'best-effort',
20+
dnslinkDataPreload: true,
2021
dnslinkRedirect: false,
2122
recoverFailedHttpRequests: true,
2223
detectIpfsPathHeader: true,

add-on/src/options/forms/dnslink-form.js

+11
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ const switchToggle = require('../../pages/components/switch-toggle')
77

88
function dnslinkForm ({
99
dnslinkPolicy,
10+
dnslinkDataPreload,
1011
dnslinkRedirect,
1112
onOptionChange
1213
}) {
1314
const onDnslinkPolicyChange = onOptionChange('dnslinkPolicy')
1415
const onDnslinkRedirectChange = onOptionChange('dnslinkRedirect')
16+
const onDnslinkDataPreloadChange = onOptionChange('dnslinkDataPreload')
1517

1618
return html`
1719
<form>
@@ -47,6 +49,15 @@ function dnslinkForm ({
4749
</option>
4850
</select>
4951
</div>
52+
<div>
53+
<label for="dnslinkDataPreload">
54+
<dl>
55+
<dt>${browser.i18n.getMessage('option_dnslinkDataPreload_title')}</dt>
56+
<dd>${browser.i18n.getMessage('option_dnslinkDataPreload_description')}</dd>
57+
</dl>
58+
</label>
59+
<div>${switchToggle({ id: 'dnslinkDataPreload', checked: dnslinkDataPreload, disabled: dnslinkRedirect, onchange: onDnslinkDataPreloadChange })}</div>
60+
</div>
5061
<div>
5162
<label for="dnslinkRedirect">
5263
<dl>

add-on/src/options/page.js

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ module.exports = function optionsPage (state, emit) {
8080
})}
8181
${dnslinkForm({
8282
dnslinkPolicy: state.options.dnslinkPolicy,
83+
dnslinkDataPreload: state.options.dnslinkDataPreload,
8384
dnslinkRedirect: state.options.dnslinkRedirect,
8485
onOptionChange
8586
})}

0 commit comments

Comments
 (0)