SignalR - Connection stopped when multiple tabs are opened - Message is incomplete - javascript

On the frontend of my application I use the #microsoft/signalr library to send off requests to my backend hubs. This works okay in a single tab, but when a second tab is open an error is outputted to the console of the first tab and the connection stops. It's almost as if the second tab is taking over for some reason, when really I believe both should be connected to the hub on different connections with different connection ids. The error I receive in the console is as follows:
[2021-04-23T09:34:22.762Z] Information: WebSocket connected to wss://localhost:6001/ptwHub?id=39HKWle3NU0eTjFxcNWsiQ.
Utils.js:218 [2021-04-23T09:34:32.895Z] Error: Connection disconnected with error 'Error: Message is incomplete.'.
./node_modules/#microsoft/signalr/dist/esm/Utils.js.ConsoleLogger.log # Utils.js:218
./node_modules/#microsoft/signalr/dist/esm/HttpConnection.js.HttpConnection.stopConnection # HttpConnection.js:542
transport.onclose # HttpConnection.js:479
./node_modules/#microsoft/signalr/dist/esm/WebSocketTransport.js.WebSocketTransport.close # WebSocketTransport.js:189
webSocket.onmessage # WebSocketTransport.js:129
ptw.ts:22 Error: Message is incomplete.
at Function../node_modules/#microsoft/signalr/dist/esm/TextMessageFormat.js.TextMessageFormat.parse (TextMessageFormat.js:13)
at JsonHubProtocol../node_modules/#microsoft/signalr/dist/esm/JsonHubProtocol.js.JsonHubProtocol.parseMessages (JsonHubProtocol.js:36)
at HubConnection../node_modules/#microsoft/signalr/dist/esm/HubConnection.js.HubConnection.processIncomingData (HubConnection.js:507)
at WebSocketTransport.HubConnection.connection.onreceive (HubConnection.js:73)
at WebSocket.webSocket.onmessage (WebSocketTransport.js:126)
The code to connect to the hub is as follows:
let connection = new signalR.HubConnectionBuilder().withUrl("https://localhost:6001/hub").build();
connection.on('knowledgeBaseUpdated', (userId: number) => {
alert(`User ${userId} has updated the knowledge base. Please refresh.`);
});
connection.start().catch(err => console.log(err));
Really not sure why the second tab would just take over the connection instead of creating a new connection. There is obviously an issue here somewhere but I'm really not sure where so any help would be really appreciated.
EDIT
I've enabled client side debugging for signalr and get the following output. The line which starts with HttpConnection.stopConnection is the line which appears when a new connections is attempted from a different tab.
[2021-04-26T09:50:00.084Z] Debug: Starting HubConnection.
Utils.js:228 [2021-04-26T09:50:00.096Z] Debug: Starting connection with transfer format 'Text'.
Utils.js:228 [2021-04-26T09:50:00.099Z] Debug: Sending negotiation request: https://localhost:6001/ptwHub/negotiate?negotiateVersion=1.
Utils.js:228 [2021-04-26T09:50:00.365Z] Debug: Selecting transport 'WebSockets'.
Utils.js:224 [2021-04-26T09:50:00.667Z] Information: WebSocket connected to wss://localhost:6001/ptwHub?id=14d1O9-cj544_-SHeZ2fgw.
Utils.js:228 [2021-04-26T09:50:00.667Z] Debug: The HttpConnection connected successfully.
Utils.js:228 [2021-04-26T09:50:00.668Z] Debug: Sending handshake request.
Utils.js:224 [2021-04-26T09:50:00.669Z] Information: Using HubProtocol 'json'.
Utils.js:228 [2021-04-26T09:50:13.079Z] Debug: Server handshake complete.
Utils.js:228 [2021-04-26T09:50:13.081Z] Debug: HttpConnection.stopConnection(Error: Message is incomplete.) called while in state Connected.
Utils.js:218 [2021-04-26T09:50:13.081Z] Error: Connection disconnected with error 'Error: Message is incomplete.'.
./node_modules/#microsoft/signalr/dist/esm/Utils.js.ConsoleLogger.log # Utils.js:218
./node_modules/#microsoft/signalr/dist/esm/HttpConnection.js.HttpConnection.stopConnection # HttpConnection.js:542
transport.onclose # HttpConnection.js:479
./node_modules/#microsoft/signalr/dist/esm/WebSocketTransport.js.WebSocketTransport.close # WebSocketTransport.js:189
webSocket.onmessage # WebSocketTransport.js:129
Utils.js:228 [2021-04-26T09:50:13.082Z] Debug: HubConnection.connectionClosed(Error: Message is incomplete.) called while in state Connecting.
Utils.js:228 [2021-04-26T09:50:13.083Z] Debug: Hub handshake failed with error 'Error: Message is incomplete.' during start(). Stopping HubConnection.
Utils.js:228 [2021-04-26T09:50:13.083Z] Debug: Call to HttpConnection.stop(Error: Message is incomplete.) ignored because the connection is already in the disconnected state.
Utils.js:228 [2021-04-26T09:50:13.084Z] Debug: HubConnection failed to start successfully because of error 'Error: Message is incomplete.'.
When the connection starts on a particular tab, I can see the connection has started and I get a single message when I look at the Network tab of Chrome Dev Tools. When I open the second tab, another message is sent to the first time which looks like an error message.. it weirdly contains a load of red dot characters? I
mage attached below. Please ignore the fact it's called ptwHub as I removed the ptw part previously to make it easier for the readers to understand.

Related

Paho MQTT JS client loses connection to Mosquitto broker when I publish or receive message (error AMQJS0005E)

Bottom line up front: The Paho MQTT client sucessfully connects to my Mosquitto broker, but immediately disconnects when I try to publish a message or when it receives a message from a topic it's subscribed to. I've tried changing Mosquitto's listening port and authentication settings, and using two different versions of Paho MQTT, and I still have the same problem.
Now let's get into detail.
Intro: I'm making a dashboard for some facial recognition devices that communicate through MQTT. I set up a Mosquitto broker and I've had no problems connecting to it and communicating with the devices using the Paho MQTT client for Python (I made a kind of server to sync the device's info to a database). Now I'm making the web interface, so I added a WebSockets listener to my mosquitto.conf and wrote a script using the Paho MQTT library for Javascript to connect to it, subscribe to topic sgdrf/out, send a simple JSON message to topic sgdrf/in to get the list of online devices, and process the response of the Python server once it arrives.
Problem and attempted solutions: I ran the Django server, loaded the web page and opened the JS console to find that the MQTT client successfully connected to the broker but immediately disconnected when it tried to publish the message to topic sgdrf/in. Here's each line of console output with their explanations:
The message produced by the onSuccess function, which indicates that the client successfully connected to the Mosquitto broker:
Conexión exitosa al broker MQTT.
In the onConnected function, I added console.log(uri) to see the URI used by the client to connect to the broker. I got:
ws://localhost:61613/
After printing uri to console, I made the client subscribe to sgdrf/out and then print 'subscribed' to console:
subscribed
Then I call get_online_devices(mqtt_client), a function which creates a simple JSON string and publishes it to the topic sgdrf/in. But first, it prints the strign to the console so that I can check it (just in case):
{"operator":"GetOnlineDevices","messageId":96792535859850080000,"info":{}}
Then, when the publish method is actually executed, is when I get this error (captured by the onConnectionLost function):
Pérdida de conexión con el broker MQTT: AMQJS0005E Internal error. Error Message: message is not defined, Stack trace: No Error Stack Available (código: 5)
I checked the Mosquitto log file and it only says when a new client was connected and then when it was disconnected because of a socket error (each time for every page reload). Tail of /var/log/mosquitto/mosquitto.log:
1614796149: New connection from 127.0.0.1 on port 61612.
1614796149: New client connected from 127.0.0.1 as mqttx_53195902 (p2, c1, k60, u'admin').
1614796182: Socket error on client sgdrf_dashboard_8499, disconnecting.
1614796325: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_1597 (p2, c1, k60, u'admin').
1614796325: Socket error on client sgdrf_dashboard_1597, disconnecting.
1614796336: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_6565 (p2, c1, k60, u'admin').
1614796336: Socket error on client sgdrf_dashboard_6565, disconnecting.
1614796931: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_9773 (p2, c1, k60, u'admin').
1614796931: Socket error on client sgdrf_dashboard_9773, disconnecting.
1614797168: Saving in-memory database to /var/lib/mosquitto/mosquitto.db.
I tried changing the listening port in mosquitto.conf, and enabling and disabling authentication, but it changes nothing. And obviously I've had to restart Mosquito every time I changed the config file. I don't think the problem is Mosquitto.
I have the same problem whether I use Paho MQTT version 1.1.0 or 1.0.3.
As an experiment, I commented out the call to get_online_devices in my Javascript so that it doesn't try to publish anything, reloaded the page and there was no error, as expected. Then, I used MQTTX to send a JSON message to the sgdrf/out topic to which the MQTT JS client is subscribed to, and it immediately disconnected with the same error message.
Code: At the bottom of the page (index.html) I have the following code (the original code has Django template tags to fill in some values, so this is the actual code received by the browser):
<!-- Paho MQTT -->
<script src="/static/js/paho-mqtt-min.js"></script>
<!-- Scripts -->
<script src="/static/js/dashboard.js"></script>
<script>
function get_online_devices(mqtt_client) {
cmd = {
operator: "GetOnlineDevices",
messageId: generate_random_number_n_exp(20),
info: {}
};
payload_string = JSON.stringify(cmd);
console.log(payload_string)
mqtt_client.publish("sgdrf/in", payload_string);
}
function add_device_to_list(device) {
// Omitted for brevity. It's not being used yet.
}
let mqtt_client = make_mqtt_client("localhost", 61613);
let connection_options = make_connection_options(
"admin",
"CENSORED_PASSWORD"
);
mqtt_client.onConnected = function(reconnect, uri) {
console.log(uri)
mqtt_client.subscribe("sgdrf/out");
console.log('subscribed');
get_online_devices(mqtt_client);
};
mqtt_client.onConnectionLost = mqtt_client_on_connection_lost;
mqtt_client.onMessageDelivered = mqtt_client_on_message_delivered;
mqtt_client.onMessageArrived = function (msg) {
// Omitted for brevity. Checks if the payload is a
// JSON object with the right data and calls
// add_device_to_list for each item of a list in it.
};
$(document).ready(function() {
mqtt_client.connect(connection_options);
$("#reload-device-list-btn").click(function() {
get_online_devices(mqtt_client);
});
});
</script>
The dashboard.js files mentioned above just has some functions that I think will be useful for other pages, so I separated them to a file:
// dashboard.js
function generate_random_number_n_exp(n) {
return parseInt(Math.random() * Math.pow(10, n), 10)
}
function make_mqtt_client(host, port) {
let client_id = "sgdrf_dashboard_" + generate_random_number_n_exp(4);
return new Paho.Client(host, port, '/', client_id);
}
function make_connection_options(user, password) {
let connection_options = {
userName: user,
password: password,
onSuccess: mqtt_client_on_success,
onFailure: mqtt_client_on_failure,
};
return connection_options;
}
function mqtt_client_on_success() {
console.log('Conexión exitosa al broker MQTT.');
}
function mqtt_client_on_failure(error) {
console.log(
'Fallo de conexión con el broker MQTT: ' + error.errorMessage
+ ' (código: ' + error.errorCode + ')'
);
}
function mqtt_client_on_connection_lost (error) {
console.log('Pérdida de conexión con el broker MQTT: ' + error.errorMessage
+ ' (código: ' + error.errorCode + ')'
);
}
function mqtt_client_on_message_delivered(msg) {
let topic = message.destinationName;
let payload = message.payloadString;
console.log("Mensaje enviado a " + topic + ": " + payload);
}
function mqtt_client_on_message_arrived(msg) {
let topic = message.destinationName;
let payload = message.payloadString;
console.log("Mensaje recibido de " + topic + ": " + payload);
}
Here are the contents of my mosquitto.conf file:
per_listener_settings true
listener 61612
allow_anonymous false
password_file /home/s8a/Projects/sgdrf/config/pwdfile.txt
listener 61613
protocol websockets
allow_anonymous false
password_file /home/s8a/Projects/sgdrf/config/pwdfile.txt
It just sets up a TCP listener and a WebSockets listener, both disallow anonymous connections, and authenticate using a pwdfile. As I said before, I have enabled and disabled anonymous connections, and changed the port number to 9001 and back to 61613, and I still have the same error.
Conclusion: I don't know what to do and this project's deadline is next week.
I feel kinda stupid, because it was really a trivial typing mistake. The problem is that the onMessageDelivered and onMessageArrived functions have msg as argument, but I wrote messagein the function body for some reason. That's what the "message is not defined" error meant, message is literally not defined. Anyway I fixed that and now it sends and receives messages without problems.
...
More detailed story: What was not trivial is how I figured it out.
I decided to get my hands dirty and opened the non-minified version of paho-mqtt.js. I looked for "Invalid error" and found where the error constant is defined, and two places where it's used in a catch block. In both catch blocks I noticed that there was a ternary operator checking if (error.hasOwnProperty("stack") == "undefined") but the true and false clauses were inverted, which is why I was getting "No Error Stack Available".
So I inverted the clauses, and indeed I got a stack trace in the console (maybe I should file a bug report to the Paho dev team when I can). The stack trace had my mqtt_client_on_message_delivered function right at the top, so I read it again and suddenly everything made sense. Then I felt stupid for wasting an afternoon on this.

A way to stop WebSocket errors to show up in browser's console

So the problem is when I try to initiate a new WebSocket to a remote host, sometimes the browser's console prints a red error message and complains about a refused connection, here is the message:
Error in connection establishment: net::ERR_CONNECTION_REFUSED
Having an error is fine since the remote host might be not responding sometimes, but the fact that I cannot handle this error message is very annoying.
Is there any way to either handle this error message or check whether the remote host accepts the WebSocket connection before initializing one in my JavaScript code??
Several possibilities come to mind:
Add a WebSocket.onerror error handler
myWebSocket.onerror = myEventHandler;
Wrap your "connect" in a try/catch block
try {
const connection = new WebSocket(myUrl);
...
}
catch(error) {
console.error(error);
}
Structure your code such that your I/O is event driven:
https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Examples
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
// Handle errors
socket.addEventListener('error', function (event) {
console.log('WebSocket error observed:', event);
});
ADDENDUM:
The above methods allow you to completely handle a websockets exception.
Regardless of whether the exception is handled or not, the Chrome debugger will tell you if an exception has occurred. This is a Good Thing. It's called a "First-Chance Exception":
https://learn.microsoft.com/en-us/security-risk-detection/concepts/first-chance-exception
.. it is known a “first chance” exception – the debugger is given the
first chance of inspecting the exception prior to the application
handling it (or not).
In Microsoft's Visual Studio debugger, there's a little checkbox you can use to "gag" first chance exceptions. I'm not aware of any similar "checkbox" in Chrome debugger.
POSSIBLE SUGGESTIONS:
Chrome debugger has a "filter". EXAMPLE FILTER REGEX: ^((?!ERR_CONNECTION_REFUSED).)*$
This link suggests you might be able to use the filter to "Hide Network Messages" (I haven't tried it myself). See also this link.

Pouchdb changes to remote Couchdb fails with ETIMEDOUT (proxy in the middle)

I have an angularjs web app that communicate with a remote CouchDB using PouchDB API, and in the middle there is a Node.js proxy (express-http-proxy) that intermediate between them.
When I try to get changes of a document through the homonym method, the request fails after some time (about 30 seconds) with ETIMEDOUT error, as in the following case:
USER INFO error: Error: ETIMEDOUT user.service.js:190
at XMLHttpRequest.xhr.onreadystatechange (pouchdb.js:11051)
at timeoutReq (pouchdb.js:10995)
In my code I call the .changes() method with the following parameters:
db.changes({
since: 'now',
live: true,
include_docs: true,
doc_ids: [userDocId],
heartbeat: 1000
}).on('change', function(change) {
console.log("USER INFO change:",change);
}).on('complete', function(info) {
console.warn("USER INFO complete:",info);
}).on('error', function(err) {
console.error("USER INFO error:",err);
});
If I also set the timeout parameter, the method fails after the number of milliseconds that I set.
If a change arrives within the time the timeout error is postponed for another ~30 seconds. After the timeout error the other changes do not arrive anymore.
I use .changes() also for another request but for previous changes and all goes fine because it isn't live.
I would like it to remain active indefinitely but it seems that the heartbeat does not work as it should; is there any configuration or parameter missing that I should set?

Cancel WebSocket connection when trying to connect (JavaScript)

Is it possible to cancel WebSocket connection while trying to establish connection to the server?
Let's say user notified that it is a misspelled host and want to cancel request for establishing connection before onerror has raised like
failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
I tried to call close, but this does not cancel request. I even get warning in console:
failed: WebSocket is closed before the connection is established.
Unfortunately, this is not achievable using close(), and seems not possible at all.
Also, unlike XMLHttpRequest, WebSocket have no abort method to achieve this.
The WebSocket specs do not mention any way of doing this, and setting the object to null does not do the trick.
The following example illustrates this by setting the WebSocket object to null, but still getting connection error message.
var ws = new WebSocket('ws://unknownhost.local');
ws.onopen = function() {
console.log('ohai');
};
ws = null;
console.log(ws);
// > null
// > VM2346:35 WebSocket connection to 'ws://unknownhost.local/' failed: Error in connection establishment: net::ERR_NAME_NOT_RESOLVED
Since the two previous answers to this question have been posted the behaviour of close() seems to have changed.
Quoting the mozilla API docs:
The WebSocket.close() method closes the WebSocket connection or connection attempt, if any. If the connection is already CLOSED, this method does nothing.
You could therefore do this:
var ws = new WebSocket('ws://unknownhost.local');
setTimeout(() => {
if (ws.readyState == WebSocket.CONNECTING) {
ws.close();
console.log("Connection cancelled.");
}
}, 1000);
Which would cancel the connection attempt if the readyState did not switch to OPEN yet.
I tried this, and it seems to work (tested on Firefox).
close() method only terminates the established connections.
For your case, you may use assigning your websocket handle to null where you want to stop it.
webSocketHanle = null;
By this assignment, your callbacks will not be activated.
But notice that it is quite fast process to getting response from server.

websocket-rails, websocket handshake error

I followed the docs on the websocket-rails github wiki page, but couldn't overcome this difficulty, I get this error with chrome:
WebSocket connection to 'ws://0.0.0.0:3000/websocket' failed: Error during WebSocket handshake: Unexpected response code: 301
I installed the websocket-rails gem, generated the install, I set successfully config.middleware.delete Rack::Lock in development.rb; I'm using Rails 4.
I got in the view:
<script type="text/javascript">
var dispatcher = new WebSocketRails('0.0.0.0:3000/websocket');
</script>
I got in my chat_controller.rb:
class ChatController < WebsocketRails::BaseController
def initialize_session
puts "Session Initialized"
end
def user_connected
puts 'user connected'
end
end
in my events.rb:
WebsocketRails::EventMap.describe do
subscribe :client_connected, :to => ChatController, :with_method => :user_connected
end
As you can see, my goal is to display "Session Initialized", and "user connected", in my thin server console each time that someone goes on a page. Between, I run the server with bundle exec thin start, but I got a javascript error instead (websocket connection failed error 301).
Thanks to the members of the Websocket Rails IRC, I found out that in my route, I was appending the locale before every path, so it coudln't find it.
Check your routes.rb if someone hit that issue too !

Categories

Resources