Javascript: Get BroadcastChannel Subscriber Count - javascript

How do I tell if Broadcast channel exists and its subscriber count?
We have a product link, and some chrome page tabs are subscribing to a Broadcast channel. Want to see how many are listening.
const bc = new window.BroadcastChannel('channel_name');
Resources:
https://medium.com/javascript-in-plain-english/using-javascript-and-the-broadcast-channel-api-a3134739781d
Communication between tabs or windows
Using Angular Typescript environment,maybe it has a library function

There is no built-in way to get the count of active ports connected through the same BroadcastChannel.
You could set up something on your own, e.g by using some pinging method where all the ports would respond and the asker just has to count the responses in a given time, but that's a bit cumbersome, makes everything async and actually using LocalStorage just for this count seems like the easiest way.
You can keep in the localStorage the current count of each channels you'll open.
Before each new connection the page can just check that value, since it will shared across the contexts that can communicate through the BroadcastChannel.
const active_connections = localStorage[ channel_name ] || 0;
Then when connecting, it just has to update that value.
if( active_connections < max_count ) {
const channel = new BroadcastChannel( channel_name );
localStorage[ channel_name ] = active_connections + 1;
}
And the trickiest being to decrease that count when the channel gets closed. For that you will need to hook to the beforeunload event:
addEventListener( 'beforeunload', (evt) => {
localStorage[ channel_name ] --;
} );
Note that if your code does call channel.close(), then you should add a line to update that count there too, and to disable the beforeunload one.
(You could still run the pinging way too to ensure the counts are still correct, since e.g if a page crashes, beforeunload could have not fired).
I should note I can't see why you'd need to do something like this, probably something is off in your design, you should double check it, and maybe open a new question about it)

Related

How to connect to several WebSockets providing the same data for redundancy on Javascript so that if some fail, others will ensure things still work?

The intention is to have several websockets for redundancy of the same data, so that if one fails, the others will still do the job. In particular, a setup where any one websocket is enough to keep the system working, even if all others fail to connect or return errors.
This is the curent setup, but I know it's wrong:
let queue = []
ws = new WebSocket(`wss://firstsource.com/ws/`);
ws2 = new WebSocket(`wss://secondsource.com/ws/`);
ws3 = new WebSocket(`wss://thirdsource.com/ws/`);
ws4 = new WebSocket(`wss://fourthsource.com/ws/`);
ws.onopen = sendIntro;
ws2.onopen = sendIntro;
ws3.onopen = sendIntro;
ws4.onopen = sendIntro;
ws.onmessage = insertQueue;
ws2.onmessage = insertQueue;
ws3.onmessage = insertQueue;
ws4.onmessage = insertQueue;
function sendIntro() {
ws.send('{"config":"2.0"}')
ws2.send('{"config":"2.0"}')
ws3.send('{"config":"2.0"}')
ws4.send('{"config":"2.0"}')
ws5.send('{"config":"2.0"}')
}
function insertQueue(msg) {
//let's just assume all we want is add the data to a queue
queue.push(msg);
}
What's the right way?
Assume the format of the data is identical for all sources.
I considered creating an onError function and use that to connect to the second websocket, and then another onError2 to connect to the third websocket, and so on, but it takes a long time to trigger onError when there is no response, so the complete process of testing a few sources if done in series like that would take far too long.
Found a working solution.
I figured all I needed was to ensure that at least one of them connect, not to ensure that if one of them disconnects the others will be ready to take over.
For this reduced problem, this worked:
//defined higher up
let sucessfulFirstSocketConnection = false
function sendIntro1() {
if(!sucessfulFirstSocketConnection){
sucessfulFirstSocketConnection = true
ws.send(sendString)
ws.onmessage = insertQueue;
ws2.close();
ws3.close();
ws4.close();
ws5.close();
}
}
function sendIntro2() {
if(!sucessfulFirstSocketConnection){
sucessfulFirstSocketConnection = true
ws2.send(sendString)
ws2.onmessage = insertQueue;
ws.close()
ws3.close();
ws4.close();
ws5.close();
}
}
and so on.
I basically let them compete among themselves, and the first one that connects closes the other connections and stops all others from getting in the way. This is achieved very quickly even if only the last WebSocket is the one that is able to connect and all others don't establish a connection.

How to prevent users from affecting number of button clicked times

I have a game written in JavaScript and what it basically does is start a ten seconds timer and register the number of times the user is able to click on a certain button, before the timer elapses.
How the code works:
When a user clicks on the button, an element gets added to an array, using push function, then a different function returns the length of the array as the number of times clicked.
The problem with this:
If a user opens up the dev tools and alters the number of times an element is added to the array per click, this will change the outcome of the result.
My Approach:
What I decided to do is to store the length before I ran the push function and also after I ran the push function, then compare their differences and if it's greater than 1, it means something is not right. This seemed to work in my head until I wrote it down in code and discovered that if the user pushed multiple times before I checked the differences then it would go unnoticed. Please big brained guys, help me.
My code:
$('body').on('click', '.btn.z', function () {
// start listening
startCountingClicks()
})
var timerStarted = false;
var tc = [];
function startCountingClicks () {
$('.btn.z').html('ClickZed');
$('.Score').html('Your Score: '+gettc()+" clicks");
if (timerStarted == false) {
startTimer(10, $('#time'));
}
// user does multiple push before this function: startCountingClicks is called
var previous_length = tc.length; // get length before push
tc.push(1);
var new_length = tc.length; // get length after push
if (new_length - previous_length !== 1) {
console.log("fraud"); // this is supposed to catch a thief!!!
}
console.log(new_length+" "+previous_length);
timerStarted = true;
}
function gettc (){
// get number of clicks
return tc.length ;
}
A code that totally breaks this:
$('button').click(function(){tc.push(1); tc.push(1)})
EDIT:
I do not wish to protect against dev tools, though I am not against that method if it works. I just wish to get a better way of counting my clicks, a way that can't be affected by writing code on the dev tools.
You can't really stop people from doing stuff on the client side. It is pointless trying to prevent that. The best thing you can do is make sure whatever is sent matches what you expect on the server side.

Push notification server backend for iOS and Android Interval Poll methods

I am writing a NodeJS push notification server for iOS and Android. Currently, once I get the device tokens I save them in a local MongoDB database and then fire notifications when there is a change. The problem is, that the database where the "change"(information is added) happens isn't my own. It's a third party's server. So the way I am aware of the change in the server is by using an API provided by the third party. My current solution is using setTimeout to make a request every X minutes to check for a change and then firing a notification based on that. The shorted interval is 10 minutes and the longest one is 1 hour. I change the intervals
dynamically through out the day based on time. My question(s) is (are),
1.) Is the setTimeout method the best possible solution to this problem? If not what else can I use?
2.) Is there any way I can implement Web Sockets in this scenario?
3.) If setTimeout is the only option, what kind of problems should I expect to run into?
This what the current function looks like
function start_notifications_server_driver() {
if(current_user_info.num_sent <= current_user_info.frequency){
//I removed the interval object on here because it's quite large and would take up space here
for(var i = 0; i < intervals.length; i++){
if(check_if_time_between(intervals[i])){
if(dev_mode) console.log("Returned true for", intervals[i]);
temp_interval = intervals[i].frequency;
break;
}
}
check_updates_for_channels_driver();
setTimeout(start_notifications_server_driver, temp_interval);
console.log("Time Interval changed to now", temp_interval);
}
}

Tab-to-tab communication using BreezeJS and localStorage

I'm using BreezeJS and storing/restoring data in local storage. That's working great. The problem occurs when the user opens multiple tabs. Changes in each tab clobber each other. Changes should be synchronised between tabs.
NB: BreezeJS will take care of merging changes, I just need to deal with race conditions between tabs.
var stashName = 'stash_everything';
window.setInterval(function () {
var exportData = manager.exportEntities();
window.localStorage.setItem(stashName, exportData);
}, 5000);
addEvent(window, 'storage', function (event) {
if (event.key == stashName) {
var importData = window.localStorage.getItem(stashName);
manager.importEntities(importData);
}
});
I've tried listening to the 'storage' event, but I haven't been able to get it working successfully. I either still clobber changes, or get into an infinite loop.
The crux of the issue is that I'm just saving on a timer; if I only saved after user interaction, then I'd avoid (most) race conditions. There's no 'has the user changed anything since last time I asked you' call in breeze, though, as far as I can tell.
Does anyone have advice on how to approach this?
Hmm this doesn't seem like it is impervious to having problems for many reasons but the main one would be that you still won't prevent concurrent saves from each tab with different data sets. That being said, if you are comfortable with the fact the two caches could be out of sync just use some unique identifier -
Somewhere in your app on load -
var stashName = 'stash-everything-' + new Date().getTime();
window.setInterval(function () {
var exportData = manager.exportEntities();
window.localStorage.setItem(stashName, exportData);
}, 5000);
Now each tab would have a unique set of data to work with.

How do I make a webRequest Event Page that only listens for events when a flag is set?

I am making an extension that can be ON or OFF, which I can check via a flag stored in local data.
When ON, I want to listen for all webRequests and redirect them like so:
chrome.webRequest.onBeforeRequest.addListener(
// callback
function(info) {
console.log("Got request: " + info.url + "\n Going to redirect");
return {redirectUrl: chrome.extension.getURL("redirect.html")};
},
// filters
{
urls: [
"<all_urls>"
]
},
// extraInfoSpec
["blocking"]);
But when OFF I do not want the event to fire at all. Ideally when OFF I wouldn't even be listening to events (if that would cut down on performance overhead in any significant way). One option I see is checking the flag in my callback function and simply not redirecting if OFF, but that still has the event being handled. As I understand it, the event will not be handled if the RequestFilter does not pass. Can I modify the RequestFilter to also check my ON/OFF boolean flag before trying to handle the event? Or is the RequestFilter only meant for checking URLs, headers, etc.?
Also my main reasoning for wanting to only handle events when ON is that it seems like a needless performance hit to try to handle EVERY webRequest - even if briefly. Would an immediate flag checking in the callback function not make any noticeable impact on performance anyway?
I am new to Chrome Extension dev and webdev in general, so if there is a much cleaner/easier way of doing this then please let me know.
Thanks!
The chrome.webRequest API cannot be used on event pages.
If you implement ExpertSystem's answer, then your extension won't add overhead to requests, but it will still waste memory (because using the webRequest API implies that you're using background pages. These pages always remain active even when the extension appears to do nothing).
The chrome.declarativeWebRequest is similar to the webRequest API, except that its API is declarative, allowing it to be used on event pages as well. The only downside of the API is that it is currently only enabled on the beta or dev channel. It will eventually be available on the stable channel though, probably within a few releases.
The following example shows how to redirect any URL whose host contains "google" ("google.com", "www.google.nl", but NOT "notgoogle.com") to a page within your extension:
var rules = [{
id: 'redirect-to-my-extension',
conditions: [
new chrome.declarativeWebRequest.RequestMatcher({
url: {
hostContains: '.google.'
}
})
],
actions: [
new chrome.declarativeWebRequest.RedirectRequest({
redirectUrl: chrome.runtime.getURL('redirect.html')
})
]
}];
// Whenever you're ready...
chrome.declarativeWebRequest.onRequest.addRules(rules);
// To disable the rules, simply remove the rules (by the previously specified id)
var ruleIds = rules.map(function(rule) { return rule.id; });
chrome.declarativeWebRequest.onRequest.removeRules(ruleIds);
This is merely an example. The declarativeWebRequest API has lots of other ways to construct conditions or actions, just take a look at the reference documentation and URL filters.
RequestFilters do not allow you to specify arbitrary conditions (such as if a flag is set). You can un-register the listener using removeListener() whenever the flag is set to OFF and register it back when the flag is set to ON. E.g.:
function myListener(...) {...}
function setEnabled(enabled) {
localStorage.enabled = enabled ? 'ON' : 'OFF';
if (enabled) {
chrome.webRequest.onBeforeRequest.addListener(myListener);
} else {
chrome.webRequest.onBeforeRequest.removeListener(myListener);
}
}
function isEnabled() {
return (localStorage.enabled !== 'OFF'); // <-- 'ON' by default
}
setEnabled(isEnabled());

Categories

Resources