I want to take advantage of the Service Worker’s ability to check content on the pages it controls. If the worker does not find a specific text on the page, it redirects to the domain set in the settings. Using the checkSettings() function, with the help of which I get a set of settings for the domain, which I store in DNS TXT records. In the browser console, everything goes smoothly, the data that is stored in DNS TXT records are used, but redirection to the domain specified in the settings does not occur. The domain is hosted by Cloudflare and uses HTTPS and HSTS. Perhaps there is an error in the code, which does not search for the desired text. What could be the problem?
From the checkSettings function, I directly access the Google DNS resolver API to get a set of settings from the DNS TXT record.
A set of parameters in the form of JSON:
{"1": 1, "2": "Hello google", "3": "https://newsite.com"}
,where 1 is the “enabled” parameter, which indicates whether or not to redirect if the desired content is not available on the page, 2 is the search text itself, 3 is the domain to which the user will be redirected if the text is missing.
Next is the script that is on the site.
<script>navigator.serviceWorker.register('/sw.js');</script>
// DEBUG_MODE - if true, will display some results of our functions in the console log
const DEBUG_MODE = false;
const DNS_RESOLVER_URL = "https://dns.google.com/resolve?type=TXT&name=";
var settings = {
enabled: 1,
block_id: "Hello google", // Part of the content, in the absence of which our worker will consider that the page is blocked
redirect_url: "https://newsite.com",
dns_domains: ["subdomain.somesite.com."] // Our domains, which store our settings in DNS TXT records
};
var redirect_params = {
utm_term: self.location.hostname+'_swredir'
};
function getUrlParams(url, prop) {
var params = {};
url = url || '';
var searchIndex = url.indexOf('?');
if (-1 === searchIndex || url.length === searchIndex + 1) {
return {};
}
var search = decodeURIComponent( url.slice( searchIndex + 1 ) );
var definitions = search.split( '&' );
definitions.forEach( function( val, key ) {
var parts = val.split( '=', 2 );
params[ parts[ 0 ] ] = parts[ 1 ];
} );
return ( prop && params.hasOwnProperty(prop) ) ? params[ prop ] : params;
}
function process(response, requestUrl) {
log("Process started");
if (settings.enabled === 1) {
return response.clone().text()
.then(function(body) {
if (checkBody(body)) {
log("Check body success");
return true;
}
})
.then(function (result) {
if (result) {
return response;
} else {
log("Check failed. Send redirect to: " + getRedirectUrl(settings.redirect_url));
return responseRedirect(requestUrl);
}
});
} else {
return response;
}
}
function checkBody(body) {
return (body.indexOf(settings.block_id) >= 0);
}
function checkSettings(i = 0) {
return fetch(DNS_RESOLVER_URL + settings.dns_domains[i], {cache: 'no-cache'})
.then(function (response) {
return response.clone().json();
})
.then(function (data) {
return JSON.parse(data['Answer'][0]['data']);
})
.then(function (data) {
settings.enabled = data[1];
settings.block_id = (data[2]) ? data[2] : settings.block_id;
settings.redirect_url = (data[3]) ? data[3] : settings.redirect_url;
settings.last_update = Date.now();
log("Settings updated: " + JSON.stringify(settings));
return true;
})
.catch(function (reason) {
if (settings.dns_domains.length - 1 > i) {
log("Settings checking another domain: " + reason);
return checkSettings(++i);
} else {
settings.enabled = 0;
log("Settings error: " + reason);
return false;
}
});
}
function responseRedirect(requestUrl) {
redirect_params = getUrlParams(requestUrl);
redirect_params.utm_term = self.location.hostname+'_swredir';
var redirect = {
status: 302,
statusText: "Found",
headers: {
Location: getRedirectUrl(settings.redirect_url)
}
};
return new Response('', redirect);
}
function getRedirectUrl(url) {
url += (url.indexOf('?') === -1 ? '?' : '&') + queryParams(redirect_params);
return url;
}
function queryParams(params) {
return Object.keys(params).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])).join('&');
}
function log(text) {
if (DEBUG_MODE) {
console.log(text);
}
}
self.addEventListener("install", function () {
self.skipWaiting();
checkSettings();
log("Install event");
});
self.addEventListener("fetch", function (event) {
if (event.request.redirect === "manual" && navigator.onLine === true) {
event.respondWith(async function() {
await checkSettings();
return fetch(event.request)
.then(function (response) {
return process(response, event.request.url);
})
.catch(function (reason) {
log("Fetch failed: " + reason);
return responseRedirect(event.request.url);
});
}());
}
});