We're getting slammed by a multitude of requests from our own clients (known IPs) hitting an endpoint of "/assets/workers/null".
We might see 6000 requests from 25 known IP addresses in a 2 minute window and a handful of requests to this endpoint a couple hours later.
This looks like it would be related to our browser push event notification service worker, which is hosted as "/assets/workers/notification.service.worker.js" but I can't see anything that looks like it would resolve to "null".
I didn't write the original code and have limited exposure to working with browser service workers at this point and am trying to determine if there is anything that could account for the strange traffic to "/assets/workers/null". Any help is greatly appreciated.
Here's the code for the service worker:
// ╔═══╦══════════════════════════════════════════════════════════════════════════╗
// ║ @ ║ Service worker responsible to manage browser push notifications ║
// ╠═══╬══════════════════════════════════════════════════════════════════════════╣
// ║ ! ║ Change this module structure can compromise the functionality ║
// ╠═══╩══════════════════════════════════════════════════════════════════════════╣
// ║ This worker depends on the SystemUI object to be initialized, so the ║
// ║ worker should be register and initialized after the application is loaded ║
// ╚══════════════════════════════════════════════════════════════════════════════╝
'use strict';
// ╔═══╦══════════════════════════════════════════════════════════════════════════╗
// ║ § ║ Event listener for notifications received by the service worker ║
// ╚═══╩══════════════════════════════════════════════════════════════════════════╝
self.addEventListener('push', function (event) { event.waitUntil(ParseReceivedEvent(event)); });
async function ParseReceivedEvent(event) {
try {
// => ~/assets/workers/notification.service.worker.js?m=1&c=APP.Configuration.Web&v=0.1.8
const moduleId = new URL(event.currentTarget.location.href).searchParams.get('m');
// => { PushNotificationMessage } from '../push.notification.model.ts'
const data = event.data.json();
if (data.VisualMessage != undefined) {
if (data.Module == moduleId || data.Module == undefined || data.Module == 0) {
// If the client is not focused, send the message to the desktop/mobile
if (!await isClientFocused()) {
const title = data.VisualMessage.Title;
const options = BuildNotificationMessage(data);
await self.registration.showNotification(title, options);
// We mark the message as silent, since we already displayed the notification
data.Silent = true;
}
}
}
await SendClientsMessage('message', null, data);
return SendClientsMessage('log', '[Service Worker] Push Received.', data);
}
catch (ex) { console.error('[Service Worker] ParseReceivedEvent Error', ex); }
}
// ╔═══╦══════════════════════════════════════════════════════════════════════════╗
// ║ § ║ Event listener for clicks on the desktop/mobile notification pop-up ║
// ╚═══╩══════════════════════════════════════════════════════════════════════════╝
self.addEventListener('notificationclick', function (event) { event.waitUntil(ParseNotificationClick(event)); });
async function ParseNotificationClick(event) {
try {
await event.notification.close();
if (event.notification.data != undefined && event.notification.data.Url != undefined && event.action == 'open') {
const data = event.notification.data;
const urlToOpen = new URL(data.Url, self.location.origin).href;
const windowClients = await clients.matchAll({ type: 'window', includeUncontrolled: true });
let matchingClient = windowClients.find(function (item) { return item.url === urlToOpen; });
if (matchingClient) {
// There is already a client with this URL
await matchingClient.focus();
}
else {
let urlObject = new URL(urlToOpen);
let matchingClientHost = await findClientHost(urlObject.host);
// There is a client open with the same web module
if (matchingClientHost) {
await SendClientsMessage('redirect', urlObject.pathname);
}
// Check if this URL can be docked
else if (event.notification.data.Dock && event.notification.data.RecordId != undefined) {
let matchingClientDomain = await findClientHost(urlObject.hostname, true);
if (matchingClientDomain) {
SendClientMessage(matchingClientDomain, 'dock', event.notification.data.RecordId, event.notification.data);
}
// There is not client with the web module, so open another tab
else { await clients.openWindow(urlToOpen); }
}
// There is not client with the web module, so open another tab
else { await clients.openWindow(urlToOpen); }
}
}
}
catch (ex) { console.error('[ServiceWorker] Error parsing notification click', ex); }
return SendClientsMessage('log', `[Service Worker] Notification click Received (Action)="${event.action || 'None'}"`, event.notification.data);
}
// ╔═══╦══════════════════════════════════════════════════════════════════════════╗
// ║ § ║ Send a message to all listening clients with the specified data ║
// ╚═══╩══════════════════════════════════════════════════════════════════════════╝
async function SendClientsMessage(type, message, value) {
return clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (windowClients) {
windowClients.forEach(function (windowClient) {
windowClient.postMessage({ type: type, message: message, value: value });
});
});
}
function SendClientMessage(client, type, message, value) {
client.postMessage({ type: type, message: message, value: value });
}
// ╔═══╦══════════════════════════════════════════════════════════════════════════╗
// ║ § ║ Check if tab of this event is currently focused ║
// ╚═══╩══════════════════════════════════════════════════════════════════════════╝
async function isClientFocused() {
return clients.matchAll({ type: 'window', frameType: 'top-level', includeUncontrolled: true })
.then((windowClients) => {
let clientIsFocused = false;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.focused) {
clientIsFocused = true;
break;
}
}
return clientIsFocused;
});
}
// ╔═══╦══════════════════════════════════════════════════════════════════════════╗
// ║ § ║ Finds a client tag with the same URL host as the defined parameter ║
// ╚═══╩══════════════════════════════════════════════════════════════════════════╝
async function findClientHost(host, useHostName) {
return clients.matchAll({ type: 'window', frameType: 'top-level', includeUncontrolled: true })
.then((windowClients) => {
let clientFound = undefined;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
var found = (!useHostName ?
new URL(windowClient.url).host :
new URL(windowClient.url).hostname) == host;
if (found) { clientFound = windowClient; break; }
}
return clientFound;
});
}
// ╔═══╦══════════════════════════════════════════════════════════════════════════╗
// ║ § ║ Build the notification message to broadcast ║
// ╚═══╩══════════════════════════════════════════════════════════════════════════╝
function BuildNotificationMessage(data) {
const options = data.VisualMessage;
var res = {
data: options,
tag: options.Group,
icon: options.Icon,
badge: options.Badge,
image: options.Image,
body: options.Message,
message: options.Message,
requireInteraction: options.RequireInteraction
};
if (options.Actions && options.Actions.length > 0) {
res.actions = options.Actions.map(function (item) { return { action: item.Name, title: item.Title, icon: item.Icon }; });
}
return res;
}