LiteralMetaphore
LiteralMetaphore

Reputation: 661

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.

Upvotes: 3

Views: 4723

Answers (2)

JoshG
JoshG

Reputation: 6745

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".

Upvotes: 6

kalatabe
kalatabe

Reputation: 2989

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']; });

Upvotes: 1

Related Questions