Awaiting localhost to reload in ASP.NET Core - javascript

I have an ASP.NET Core server, which is using SignalR to generate HTML pages dynamically at runtime through JavaScript. After i shut my application down, in the console i can read that SignalR is disconnected:
signalr.js:2404 Error: Connection disconnected with error 'Error: Websocket closed with status code: 1006 ()'.
The question is - what do i need to do to setup SignalR to wait incoming connection again after disconnecting? Meaning that when my application will be up again, preloaded localhost page will connect automatically and continue working with the new instance of my app.
The way i initialize SignalR in JS:
var connection = new signalR.HubConnectionBuilder()
.withUrl("/foo")
.build();
And then
connection.on("state", function (state) {
// some business logics there
});
connection.start().catch(function err() {
return console.error(err.ToString());
});

As far as I know, Signalr Client library provide the auto reconnect feature. You could enable it by using the withAutomaticReconnect. Please notice: Without any parameters, WithAutomaticReconnect configures the client to wait 0, 2, 10, and 30 seconds respectively before trying each reconnect attempt. After four failed attempts, it stops trying to reconnect.
Also you could write codes to do Manually reconnect. More details, you could refer to this article.

Brando's answer lead me to do the following thing:
var connection = new signalR.HubConnectionBuilder()
.withUrl("/foo")
.build();
function start() {
connection.start().catch(function () {
setTimeout(function () {
start();
//there we can process any HTML changes like
//disabling our loader etc. start() will fail
//with exception if there's no server started
//and it will try again in 5 seconds
}, 5000);
});
}
connection.onclose(e => {
//process here any HTML changes like
//showing the loader, etc, an then
start();
})
connection.on("foo", function (state) {
// some business logics there
});
start();
It looks like typical relationship between ASP.NET and signalR + vanilla javascript

Related

Does anyone know how to define navigator online in main process in electron?

I know you can use navigator onLine inside the renderer process because it's a rendered inside a browser. But what I'm trying to do is something like this in the main process:
if (navigator.onLine){
mainWindow.loadURL("https://google.com")
} else {
mainWindow.loadFile(path.join(__dirname, 'index.html'));
}
So basically if the user is offline, just load a local html file, and if they're online, take them to a webpage. But, like expected, I keep getting the error that 'navigator is not defined'. Does anyone know how can I somehow import the navigate cdn in the main process? Thanks!
TL;DR: The easiest thing to do is to just ask Electron. You can do this via the net module from within the Main Process:
const { net } = require ("electron");
const isInternetAvailable = () => return net.isOnline ();
// To check:
if (isInternetAvailable ()) { /* do something... */ }
See Electron's documentation on the method; specifically, this approach doesn't tell you whether your service is accessible via the internet, but rather that a service can be contacted (or not even this, as the documentation mentions links which would not involve any HTTP request at all).
However, this is not a reliable measurement and you might want to increase its hit rate by manuallly checking whether a certain connection can be made.
In order to check whether an internet connection is available, you'll have to make a connection yourself and see if it fails. This can be done from the Main Process using plain NodeJS:
// HTTP code basically from the NodeJS HTTP tutorial at
// https://nodejs.dev/learn/making-http-requests-with-nodejs/
const https = require('https');
const REMOTE_HOST = "google.com"; // Or your domain
const REMOTE_EP = "/"; // Or your endpoint
const REMOTE_PAGE = "https://" + REMOTE_HOST + REMOTE_EP;
function checkInternetAvailability () {
return new Promise ((resolve, reject) => {
const options = {
hostname: REMOTE_HOST,
port: 443,
path: REMOTE_EP,
method: 'GET',
};
// Try to fetch the given page
const req = https.request (options, res => {
// Yup, that worked. Tell the depending code.
resolve (true);
req.destroy (); // This is no longer needed.
});
req.on ('error', error => {
reject (error);
});
req.on ('timeout', () => {
// No, connection timed out.
resolve (false);
req.destroy ();
});
req.end ();
});
}
// ... Your window initialisation code ...
checkInternetAvailability ().then (
internetAvailable => {
if (internetAvailable) mainWindow.loadURL (REMOTE_PAGE);
else mainWindow.loadFile (path.join (__dirname, 'index.html'));
// Call any code needed to be executed after this here!
}
).catch (error => {
console.error ("Oops, couldn't initialise!", error);
app.quit (1);
});
Please note that this code here might not be the most desirable since it just "crashes" your app with exit code 1 if there is any error other than connection timeout.
This, however, makes your startup asynchronous, which means that you need to pay attention on the execution chain of your app startup. Also, startup may be really slow in case the timeout is reached, it may be worth considering NodeJS' http module documentation.
Also, it makes sense to actually try to retrieve the page you're wanting to load in the BrowserWindow (constant values REMOTE_HOST and REMOTE_EP), because that also gives you an indication whether your server is up or not, although that means that the page will be fetched twice (in the best case, when the connection test succeeds and when Electron loads the page into the window). However, that should not be that big of a problem, since no external assets (images, CSS, JS) will be loaded.
One last note: This is not a good metric of whether any internet connection is available, it just tells you whether your server answered within the timeout window. It might very well be that any other service works or that the connection just is very slow (i.e., expect false negatives). Should be "good enough" for your use-case though.

Bi-directional communication between a client page and a service worker

Having a preact app generated by preact-cli (uses workbox), my objective is to register a 'message' event handler on the service worker, post a message from the app and finally receive a response back.
Something like:
/* app.js */
navigator.serviceWorker.postMessage('marco');
const response = ? // get the response somehow
/* sw.js */
addEventListener('message', function (e) { return 'polo' });
I don't have much experience with service workers and there are a lot of moving parts that confuse me, like workbox doing magic on service worker, preact hiding away the code that registers the sercice worker and service workers being tricky to debug in general.
So far I've placed a sw.js file in the src/ directory as instructed by the preact-cli docs here: https://preactjs.com/cli/service-worker/
I know I am supposed to attach an event listener but I can't find documentation on which object to do so.
(Neither Workbox nor Preact have much to do with this question. Workbox allows you to use any additional code in your service worker that you'd like, and Preact should as well for your client app.)
This example page demonstrates sending a message from a client page to a service worker and then responding, using MessageChannel. The relevant helper code used on the client page looks like:
function sendMessage(message) {
return new Promise(function(resolve, reject) {
const messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
// The response from the service worker is in event.data
if (event.data.error) {
reject(event.data.error);
} else {
resolve(event.data);
}
};
navigator.serviceWorker.controller.postMessage(message,
[messageChannel.port2]);
});
}
And then in your service worker, you used the MessageChannel's port to respond:
self.addEventListener('message', function(event) {
// Your code here.
event.ports[0].postMessage({
error: // Set error if you want to indicate a failure.
message: // This will show up as event.data.message.
});
});
You could also do the same thing using the Comlink library to simplify the logic.

Sending data in a timer by JS WebRTC crashes when reloading before the end

I have this simple piece of code in a server.js javascript file served by node:
function multiStep(myConnection, data) {
var i=0;
var myTimer=setInterval(function() {
if (i<data.length){
var element=JSON.stringify(data[i]);
console.log("mando elemento: "+element);
myConnection.send(element);
i++;
}
}, 3000);
}
//require our websocket library
clearInterval(myTimer);
var WebSocketServer = require('ws').Server;
//creating a websocket server at port 9090
var wss = new WebSocketServer({port: 9090});
//when a user connects to our sever
wss.on('connection', function(connection) {
loadJSON(function(response) {
//when server gets a message from a connected user
connection.on('message', function(message){
console.log("Got message from a user:", message);
});
var json = JSON.parse(response);
multiStep(connection, json, 0);
})
});
loadJSON simply loads a json data file from another web site.
When I run the client application the first time or when the timeout has ended everything goes fine. Yet if I reload the page while the timeout is not finished I get a crash when I try to use the connection of the old page on the server with report:
/var/www/html/MATERIALI/phonegap/node_modules/ws/lib/WebSocket.js:219
else throw new Error('not opened');
^ Error: not opened
at WebSocket.send (/var/www/html/MATERIALI/phonegap/node_modules/ws/lib/WebSocket.js:219:16)
at null. (/var/www/html/MATERIALI/phonegap/WebRTC/server.js:36:9)
at wrapper [as _onTimeout] (timers.js:261:14)
at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)
As a matter of fact I could simply ignore the old session given the page is reloaded. How do I avoid the server to crash in these circumstances?
Ok, I think I found the solution; function multiStep becomes:
function multiStep(myConnection, data) {
var i=0;
clearInterval(myTimer);
myTimer=setInterval(function() {
if (i<data.length){
var element=JSON.stringify(data[i]);
console.log("mando elemento: "+element);
try {
myConnection.send(element);
console.log("mandato elemento");
} catch(err) {
console.log('Websocket error: %s', err);
}
i++;
} else {
}
}, 3000);
}
And it does not crash any longer.
You need to do some checking along the way. Your code assumes that everything is 100%.
var json = JSON.parse(response);
multiStep(connection, json, 0);
You assume there is data in response (it might be empty, or contain non-json data)
You should also check that json is a valid array before passing it to multiStep
The function multiStep also assumes that data.length will return something numeric
This may not be the complete answer, but it should give you a start on making your code more robust.
It's probably failing at myConnection.send(element); but that is probably only a symptom of your lack of checking along the way (you can also check if myConnection is still valid before you send something to it)
Referring to https://developer.mozilla.org/en-US/docs/Web/API/WebSocket, you should be able to check the myConnection.readyState value:
Ready state constants
These constants are used by the readyState attribute to describe the state of the WebSocket connection.
Constant Value Description
CONNECTING 0 The connection is not yet open.
OPEN 1 The connection is open and ready to communicate.
CLOSING 2 The connection is in the process of closing.
CLOSED 3 The connection is closed or couldn't be opened.
Your code will look like this now:
console.log("mando elemento: "+element);
if (myConnection.readyState === 1)
myConnection.send(element);
else
console.log("web socket not open");

PeerJS Auto Reconnect

I have recently developed a web app using PeerJS, and am trying to add reconnect functionality.
Basically, my app works by someone creating a server that clients then connect to. The server person can control what the hosts are doing but its basic two way communication.
If a client disconnects they simply reconnect and it works normally. However if the server user refreshes the page, or their computer crashes then they need to be able to re-establish control over the clients.
The start of this is by regaining the original connection ID and peer api ID, which is fine and easy as they are stored in a database and assigned a unique ID the server user can use to query them. Then to enable the client to reconnect I do this upon close:
// connection is closed by the host involuntarily...
conn.on('close', function() {
// if the clients connection closes set up a reconnect request loop - when the host takes back control
// the client will auto reconnect...
connected = false;
conn = null;
var reconnect_timer = setInterval(function () {
console.log('reconnecting...'); // make a fancy animation here...
conn = peer.connect(connectionid, {metadata: JSON.stringify({'type':'hello','username':username})});
// upon connection
conn.on('open', function() { // if this fails need to provide an error message... DO THIS SOON
// run the connect function...
connected = true;
connect(conn);
});
// didnt connect yet
conn.on('error', function(err) {
connected = false;
});
if(connected === true) {
clearInterval(reconnect_timer);
}
}, 1000);
});
This appears to work, as on the server end the client looks like they have reconnected - the connect function has fired etc. However messages cant be sent between, and the client console says:
Error: Connection is not open. You should listen for the `open` event before sending messages.(…)
Where the 'open' event is shown as having been listened to above...
I hope this is clear - any help is appreciated :)
So in the end to create an auto reconnect script, I simply dealt with the client end of things, ensuring the server was set to the same api_key (for cloudservers) and key:
peer = new Peer(return_array.host_id, {key: return_array.api_key});
and then having the client, upon connection closing:
// connection is closed by the host involuntarily...
conn.on('close', function() {
// if the clients connection closes set up a reconnect request loop - when the host takes back control
// the client will auto reconnect...
peer.destroy(); // destroy the link
connected = false; // set the connected flag to false
conn = null; // destroy the conn
peer = null; // destroy the peer
// set a variable which means function calls to launchPeer will not overlap
var run_next = true;
// periodically attempt to reconnect
reconnect_timer = setInterval(function() {
if(connected===false && run_next===true) {
run_next = false; // stop this bit rerunning before launchPeer has finished...
if(launchPeer(false)===true) {
clearInterval(reconnect_timer);
} else run_next == true;
}
}, 1000);
});
Where launch peer will attempt to launch a new peer. To ensure continuity the new id from the client replaces the old id from the client and everything is a smooth takeover. The hardest part in the end was having the "setInterval" only fire once which is achieved (badly...) through use of boolean flags.
Thanks to anybody who read and thought how they could help :)

Storing RabbitMQ connection in NodeJs

I currently forced to create a new RabbitMQ connection every time a user loads a page on my website.
This is creating a new TCP connection every time. However, i'm trying to reduce the number of TCP connections i make to Rabbit with the NodeJS AMQP plug in. Here is what i have:
var ex_conn = get_connection(uri); //http:rabbitm.com
if(ex_conn == false) {
var tempConn = amqp.createConnection({
url: uri
});
connections.push({
host: uri,
obj: tempConn
});
}
else {
var tempConn = ex_conn.obj;
}
The issue i'm running into is that if i try to do:
tempConn.on('ready', function() {
});
Then the ready function does not get triggered. I'm assuming, that is because the ready call back was already defined and it is not going to be re triggered. What i'm looking to do is bind a new queue by doing:
tempConn.queu('', {});
Any thoughts on how to get around this issue is much appreciated.
thanks.

Categories