static/poll-fetch.js
Rama main · réplica de lectura del repositorio original.
// fetch() for endpoints a page polls on a timer: stop hammering one that
// keeps refusing.
//
// A page left open on a machine that is not in the settings whitelist
// polls forever and is refused every time — one display client produced
// 9,182 consecutive 403s in a week, about a fifth of the whole access
// log, and every one of them displaced a real request inside the log's
// row cap. The same shape appears with 404 when the two paired machines
// are on different versions: the newer one polls an endpoint the older
// one does not have yet, for as long as the version gap lasts.
//
// So after a few consecutive 403/404s the endpoint is left alone for a
// while. The caller's timer keeps ticking — it just returns a rejected
// promise without touching the network, so no page has to restructure
// its polling loop. A single success clears the state, and the pause
// expires on its own, so whitelisting the machine or finishing the
// update recovers without anyone reloading the page.
window.pollFetch = (function () {
var LIMIT = 3; // consecutive refusals before pausing
var PAUSE_MS = 10 * 60000; // how long to leave the endpoint alone
var state = {}; // path -> {denials, pausedUntil}
// Clear a pause. A button the operator just pressed is not polling —
// it must try the server again however the timer behind it is doing.
function reset(url) {
delete state[String(url).split('?')[0]];
}
var poll = function (url, options) {
var key = String(url).split('?')[0];
var entry = state[key] || (state[key] = { denials: 0, pausedUntil: 0 });
var now = Date.now();
if (entry.pausedUntil > now) {
var err = new Error('polling paused for ' + key);
err.pollingPaused = true;
return Promise.reject(err);
}
return fetch(url, options).then(function (response) {
if (response.status === 403 || response.status === 404) {
entry.denials += 1;
if (entry.denials >= LIMIT) {
entry.pausedUntil = Date.now() + PAUSE_MS;
console.warn('pollFetch: ' + key + ' returned ' +
response.status + ' ' + entry.denials +
' times — pausing polling for ' +
(PAUSE_MS / 60000) + ' minutes');
}
} else {
entry.denials = 0;
entry.pausedUntil = 0;
}
return response;
});
// A rejected fetch (server restarting, network blip) is deliberately
// not counted: that is the case where polling SHOULD keep trying.
};
poll.reset = reset;
return poll;
})();