Get response headers from websocket handshake - javascript

I'm trying to get a client to read the error message given when a connection to a WebSocket is failed.
The client attempts to connect to the socket with
this.socket = new WebSocket(url);
and the server handles this by listening for
server.on('upgrade', function upgrade(request, socket, head) {...})
If the client doesn't have the proper authentication, the server sends socket.write with a 401 error and a custom header containing a message saying why the connection failed, and then doing a socket.destroy(). After this, the client's this.socket is set to null as it isn't opened.
My question is, how can I get the client to read that custom error message so that I can display it to the user? If this isn't possible, what would be the best approach to communicate this error?

Related

Sending bytes from javascript client to java backend DatagramSocket

I have a java app that functions as a server, listening to some port using the java.net.DatagramSocket class. The server is able to receive packets from some other java clients.
What I did not manage to do is have a javascript client to the server.
I've tried sending messages as WebSocket client
const ws = new WebSocket("ws://localhost:9999");
ws.addEventListener("open", () => {
console.log("CONNECTED")
})
But I'm getting the following error in the browser console:
App.js:9 WebSocket connection to 'ws://localhost:9999/' failed:
I've also tried using net library to send message:
const net = require("net");
const options = {
post: 9999
};
const client = net.createConnection(options);
client.write("TEST")
But I'm getting the following error:
Uncaught TypeError: net.createConnection is not a function
Is there any other way to do this?
UDP (DatagramSocket) and WebSocket are totally different things.
WebSocket is a protocol on top of HTTP which is on top of TCP.
In JavaScript running inside the browser you cannot send UDP packages.
You can write some kind of a proxy server, which can receive messages via WebSocket from your JavaScript program and send them via UDP to your server.
Or extend your server with a WebSocket interface.

When to refresh token in socket.io?

Where should i call a refresh token request in websocket app (i'm using socket.io)?
Is there a way to re-emit particular socket.io event if server sent 'not authorized' error in response to the first attempt? Something like axios.interceptors for websockets?
Thank you!

Socket.IO attempting to connect through https:// instead of wss:// and getting a CORS error

I'm switching from JavaScript's vanilla WebSocket API to Socket.IO for real-time data about cryptocurrency prices. While using the regular WebSocket I had no problem connecting to Kraken and getting the data I need. However, when trying to connect with Socket.IO, I get a CORS error.
Access to XMLHttpRequest at 'https://ws.kraken.com/socket.io/?EIO=3&transport=polling&t=Mxg8_5_' from origin has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
In in the Chrome dev tools network tab, I'm getting an Invalid request response from Kraken. I assume Socket.IO is trying to send some sort of preflight request when trying to establish a websocket connection and failing due to Kraken's CORS policy for http requests. Is there a way to completely bypass this XMLHttpRequest attempt and immediately try a websocket connection, seeing as the regular WebSocket API has no issues establishing this connection and doesn't seem to send a preflight request? Here are both the vanilla and the Socket.IO sockets:
// vanilla websocket
const vanillaWS = new WebSocket('wss://ws.kraken.com');
vanillaWS.onopen = () => {
console.log('vanilla websocket opened');
}
vanillaWS.onmessage = (message) => {
console.log(message.data);
}
// socket.io websocket
const ioSocket = io('wss://ws.kraken.com');
ioSocket.on('connect', () => {
console.log('socket.io socket opened');
});
ioSocket.on('message', (message) => {
console.log(message.data);
});
As you can see, these should be functionally very similar, but while the first one works as expected, the second one is throwing the error.
From the documentation:
What Socket.IO is not
Socket.IO is NOT a WebSocket implementation. Although Socket.IO indeed
uses WebSocket as a transport when possible, it adds some metadata to
each packet: the packet type, the namespace and the packet id when a
message acknowledgement is needed. That is why a WebSocket client will
not be able to successfully connect to a Socket.IO server, and a
Socket.IO client will not be able to connect to a WebSocket server
either. Please see the protocol specification
here.
So if the endpoint you're trying to use isn't running a Socket.IO server, this isn't going to work.
That said, if it is, you can force the use of websockets using the transports parameter:
const ioSocket = io(endpoint, {
transports: ['websocket'] // forces websockets only
});
Bottom Line: Socket.IO is not a replacement for a WebSockets connection. Socket.IO uses WebSockets to accomplish its goal: "Socket.IO is a library that enables real-time, bidirectional and event-based communication between the browser and the server".
You're getting the CORS error because socket.io attempts pure HTTP-based long-polling connection first and that's what fails. You should manually set your client to attempt websocket first:
var options = {
allowUpgrades: true,
transports: ['websocket', 'polling'],
};
var sock = io(server, options);
sock.on('connect', () => {
console.log('socket.io socket opened');
});
sock.on('message', (message) => {
console.log(message.data);
});
From the socket.io docs:
With websocket transport only
By default, a long-polling connection is established first, then
upgraded to “better” transports (like WebSocket). If you like to live
dangerously, this part can be skipped:
const socket = io({ transports: ['websocket'] });
// on reconnection, reset the transports option, as the Websocket //
connection may have failed (caused by proxy, firewall, browser, ...)
socket.on('reconnect_attempt', () => { socket.io.opts.transports =
['polling', 'websocket']; });

'Can't set headers after they are sent' error coming from zeromq

My nodejs app uses zeromq to communicate with the back-end. It generally works well but in the case where I restart the back-end while the app is running it will crash reporting the message below:
events.js:163
throw er; // Unhandled 'error' event
^
Error: Can't set headers after they are sent.
at ServerResponse.setHeader (_http_outgoing.js:371:11)
at ServerResponse.header (/home/gl/Documents/client/node_modules/express/lib/response.js:592:10)
at ServerResponse.send (/home/gl/Documents/client/node_modules/express/lib/response.js:144:12)
at ServerResponse.json (/home/gl/Documents/client/node_modules/express/lib/response.js:233:15)
at exports.Socket.<anonymous> (/home/gl/Documents/client/app/routes.js:13:12)
at emitMany (events.js:127:13)
at exports.Socket.emit (events.js:204:7)
at exports.Socket.Socket._emitMessage (/home/gl/Documents/client/node_modules/zeromq/lib/index.js:640:15)
at exports.Socket.Socket._flushRead (/home/gl/Documents/client/node_modules/zeromq/lib/index.js:651:10)
at exports.Socket.Socket._flushReads (/home/gl/Documents/client/node_modules/zeromq/lib/index.js:687:15)
routes.js looks like this:
module.exports = function(app) {
var zmq = require('zeromq')
, sock = zmq.socket('dealer');
var response = null;
sock.on('message', function(data) {
response.json({data: data.toString()});
});
app.get('/data', function(req, res) {
response = res;
sock.send(['', 'low']);
});
};
This way once a message comes from the back-end it will be sent using the current response object. That's the simplest way I found to capture responses from zeromq and send back the response to my application.
What is causing that error to popup, and if it is due to the design of my code, is there a better way to integrate zeromq with my application so I send and receive messages asynchronously for the various functions of my application?
You have two servers.
ZeroMQ
Express (or some other HTTP server … at least the use of app.get('/data', function(req, res) { gives that impression)
… they are both running from within the same Node.js application.
When the browser makes an HTTP request, it expects to get an HTTP response.
You are not sending an HTTP response when you get a request from the browser, you are sending it when you get a message over ZeroMQ.
As a result of this, you are trying to respond to the same HTTP request multiple times… which is impossible.
You need to rethink the architecture of your application.
Possibly you should be using something like Socket.IO so that you can push a message from the server to the browser whenever the server is ready to do so (instead of whenever an HTTP request asks for one).

Handling unsuccessful websocket upgrade requests in Javascript Client

I want to have a javascript client process get the HTTP status code that a server is returning when the client makes a websocket upgrade request and that request is unsuccessful.
I have my server returning HTTP 400 to indicate that a websocket upgrade is unsuccessful.
I am using Google Chrome and when i open the developer console I can see the following message:
WebSocket connection to 'wss://' failed: Error during WebSocket handshake: Unexpected response code: 400
However, the onerror handler does not contain this message, it receives a 1006 error but does not indicate that the closure occured as a result of getting HTTP 400.
How does a javascript developer handle handshake errors? I would like to provide the client with an informative message when they get a handshake error.
I have put the websocket error below, it does not seem to contain anything that I can use to indicate that this error is a result of a websocket handshake error.
Websocket Error: {"path":{"length":0},"cancelBubble":false,"returnValue":true,"srcElement":{"binaryType":"blob","protocol":"","extensions":"","bufferedAmount":0,"readyState":3,"url":"wss://<my address>","URL":"wss://<my address>"},"defaultPrevented":false,"timeStamp":1417828938039,"cancelable":false,"bubbles":false,"eventPhase":2,"currentTarget":{"binaryType":"blob","protocol":"","extensions":"","bufferedAmount":0,"readyState":3,"url":"wss://<my address>","URL":"wss://<my address>"},"target":{"binaryType":"blob","protocol":"","extensions":"","bufferedAmount":0,"readyState":3,"url":"wss://<my address>","URL":"wss://<my address>"},"type":"error"}
I am afraid there is no way from Javascript to know the HTTP status code of the negotiation.
There are defined closing codes, and 1006 only means that the connection is closed abruptly, but the protocol even allows to close the connection without providing a reason. That, together with the readyState API, are the only tools you have to diagnosed the reason of the problem.

Categories

Resources