Express CORS is not working with socket.io - javascript

I've used cors for my express server, but I can't figure out why it's not working. Can anyone please help me with this issue?
Access to XMLHttpRequest at
'https://tic-tac-toe-server.now.sh/socket.io/?EIO=3&transport=polling&t=N6Z2b4X'
from origin 'http://localhost:8080' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested
resource.
Client:
import io from 'socket.io-client';
const socket = io('https://tic-tac-toe-server.now.sh')
Here is my index.js
const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const cors = require('cors');
const router = require('./router');
const { addUser, removeUser, getUsers } = require('./users');
const { getMatch, addPlayer, destroyMatch } = require('./players');
const PORT = process.env.PORT || 5000;
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(router);
app.use(cors());
io.on('connection', function (socket) {
const id = socket.id;
let user_room = '';
/**
* User Joins to the global room
*/
socket.on('join', function ({ name, room, playing = false }) {
addUser({ id, name, room, playing }); // add user to users array
user_room = room;
socket.join(user_room);
socket.join(id);
socket.emit('user_joined', getUsers());
socket.broadcast.emit('user_joined', getUsers()); // emit event with modified users array
});
/**
* Match Started
*/
socket.on('player_joined', user => {
const match = getMatch();
addPlayer(user.match, user);
if(match.hasOwnProperty(user.match) && match[user.match].length === 2){
socket.emit('player_joined', match[user.match]);
socket.broadcast.to(user.match).emit('player_joined', match[user.match]);
}
});
socket.on('move', (data) => {
socket.emit('move', data);
socket.broadcast.to(data.match).emit('move', data);
});
socket.on('emote', (data) => {
socket.emit('emote_from', data);
socket.broadcast.to(data.match).emit('emote_to', data);
});
/**
* On user challenge
*/
socket.on('challenge', (socketId) => {
io.to(socketId).emit('accept', id);
});
socket.on('rejected', (socketId) => {
io.to(socketId).emit('rejected', id);
});
socket.on('accepted', data => {
io.to(data.opponent.id).emit('accepted', data);
socket.emit('accepted', data);
});
socket.on('player_left_match', match => {
socket.broadcast.to(match).emit('player_left_match');
});
socket.on('destroy_match', match => {
destroyMatch(match);
});
/**
* User Disconnect function
*/
socket.on('disconnect', () => {
socket.leave(user_room);
socket.leave(id);
removeUser(id); // remove user form users array
socket.emit('user_left', getUsers());
socket.broadcast.emit('user_left', getUsers()); // emit event with modified users
})
});
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));

You can tell socket.io to only use the webSocket transport which is not subject to CORS by changing this:
const socket = io('https://tic-tac-toe-server.now.sh')
to this:
const socket = io('https://tic-tac-toe-server.now.sh', {transports: ['websocket']});
Some background. In its default configuration, socket.io starts every connection with multiple plain http requests. These plain http requests require server-side CORS support if the connection is cross-origin. But, socket.io can be configured to go straight to the webSocket transport (which is what is eventually ends up using anyway) and webSocket connections are not subject to CORS limitations.
The socket.io design to start with http polling was largely there because in the early days of webSocket support, not every browser supported it and not every server infrastructure supported it. But now-a-days, it is pretty widely supported.
So, telling socket.io to start with the webSocket transport from the beginning avoids many potential CORS issues.
We are now chasing a different issue and the error showing in the console at https://tic-tac-toe-vue.now.sh/ is coming from this code in webSocket.js.
try {
this.ws =
this.usingBrowserWebSocket && !this.isReactNative
? protocols
? new WebSocketImpl(uri, protocols)
: new WebSocketImpl(uri)
: new WebSocketImpl(uri, protocols, opts);
} catch (err) {
return this.emit('error', err);
}
It looks like something React related since there's a reference to isReactNative, but since your code is packaged and minimized, it's not very easy to do any debugging from here.

Related

Access from origin 'https://example.com' has been blocked even though I've allowed https://example.com/

I have an app made with React, Node.js and Socket.io
I deployed Node backend to heroku , frontend to Netlify
I know that CORS errors is related to server but no matter what I add, it just cant go through that error in the picture below.
I also added proxy script to React's package.json as "proxy": "https://googledocs-clone-sbayrak.herokuapp.com/"
And here is my server.js file;
const mongoose = require('mongoose');
const Document = require('./Document');
const dotenv = require('dotenv');
const path = require('path');
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
dotenv.config();
const app = express();
app.use(cors());
const server = http.createServer(app);
const io = socketio(server, {
cors: {
origin: 'https://googledocs-clone-sbayrak.netlify.app/',
methods: ['GET', 'POST'],
},
});
app.get('/', (req, res) => {
res.status(200).send('hello!!');
});
const connectDB = async () => {
try {
const connect = await mongoose.connect(process.env.MONGODB_URI, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
console.log('MongoDB Connected...');
} catch (error) {
console.error(`Error : ${error.message}`);
process.exit(1);
}
};
connectDB();
let defaultValue = '';
const findOrCreateDocument = async (id) => {
if (id === null) return;
const document = await Document.findById({ _id: id });
if (document) return document;
const result = await Document.create({ _id: id, data: defaultValue });
return result;
};
io.on('connection', (socket) => {
socket.on('get-document', async (documentId) => {
const document = await findOrCreateDocument(documentId);
socket.join(documentId);
socket.emit('load-document', document.data);
socket.on('send-changes', (delta) => {
socket.broadcast.to(documentId).emit('receive-changes', delta);
});
socket.on('save-document', async (data) => {
await Document.findByIdAndUpdate(documentId, { data });
});
});
console.log('connected');
});
server.listen(process.env.PORT || 5000, () =>
console.log(`Server has started.`)
);
and this is where I make request from frontend;
import Quill from 'quill';
import 'quill/dist/quill.snow.css';
import { useParams } from 'react-router-dom';
import { io } from 'socket.io-client';
const SAVE_INTERVAL_MS = 2000;
const TextEditor = () => {
const [socket, setSocket] = useState();
const [quill, setQuill] = useState();
const { id: documentId } = useParams();
useEffect(() => {
const s = io('https://googledocs-clone-sbayrak.herokuapp.com/');
setSocket(s);
return () => {
s.disconnect();
};
}, []);
/* below other functions */
/* below other functions */
/* below other functions */
}
TL;DR
https://googledocs-clone-sbayrak.netlify.app/ is not an origin. Drop that trailing slash.
More details about the problem
No trailing slash allowed in the value of the Origin header
According to the CORS protocol (specified in the Fetch standard), browsers never set the Origin request header to a value with a trailing slash. Therefore, if a page at https://googledocs-clone-sbayrak.netlify.app/whatever issues a cross-origin request, that request's Origin header will contain
https://googledocs-clone-sbayrak.netlify.app
without any trailing slash.
Byte-by-byte comparison on the server side
You're using Socket.IO, which relies on the Node.js cors package. That package won't set any Access-Control-Allow-Origin in the response if the request's origin doesn't exactly match your CORS configuration's origin value (https://googledocs-clone-sbayrak.netlify.app/).
Putting it all together
Obviously,
'https://googledocs-clone-sbayrak.netlify.app' ===
'https://googledocs-clone-sbayrak.netlify.app/'
evaluates to false, which causes the cors package not to set any Access-Control-Allow-Origin header in the response, which causes the CORS check to fail in your browser, hence the CORS error you observed.
Example from the Fetch Standard
Section 3.2.5 of the Fetch Standard even provides an enlightening example of this mistake,
Access-Control-Allow-Origin: https://rabbit.invalid/
and explains why it causes the CORS check to fail:
A serialized origin has no trailing slash.
Looks like you haven't imported the cors package. Is it imported anywhere else?
var cors = require('cors') // is missing

WebSocket connection is taking long time and failed

I created a secure websocket using this,
const Socket = require("websocket").server
const https = require("tls")
const fs = require('fs');
//certificate information
const certificate = {
cert: fs.readFileSync("/home/WebRTC/ssl/webrtc.crt",'utf8'),
key: fs.readFileSync("/home/WebRTC/ssl/webrtc.key",'utf8')
};
const server = https.createServer(certificate,(req, res) => {})
server.listen(3000, () => {
console.log("Listening on port 3000...")
})
const webSocket = new Socket({ httpServer: server })
and created the web client using this,
const webSocket = new WebSocket("wss://ip:3000")
webSocket.onerror= (event) => {
alert("Connection error occured");
}
webSocket.onopen = (event) =>{
alert("Connection established");
}
webSocket.onmessage = (event) => {
alert("Message received");
}
Im using https. Created a self signed certificate
wss://ip:3000. here the IP is the certificate resolving IP. These files are hosted in a publicly accessible server
But when I put the request, it takes a lot of time and gives and error.
"WebSocket connection to 'wss://ip:3000/' failed: "
Please be kind enough to help

Cannot Sending Twilio SMS with TwiML Webhook

The code below should send the message "TwilioQuest rules", via twilio webhook using ngrok so that it can be accessed via the internet https://765ec92dsf.ngrok.io/sms to send messages. But when the code is executed it always displays response 404. And I don't know where the error lies, because the console.log doesn't display anything.
My Code
const http = require('http');
const express = require('express');
const { urlencoded } = require('body-parser');
const ngrok = require('ngrok');
const twilio = require('twilio');
// Set up our express web application
const PORT = 8767;
const app = express();
app.use(urlencoded({ extended: false }));
// Create a route to handle incoming SMS messages
// This is where the magic happens!
app.post('/sms', (request, response) => {
console.log(
`Incoming message from ${request.body.From}: ${request.body.Body}`
);
response.type('text/xml');
response.send(`
<Response>
<Message>TwilioQuest rules</Message>
</Response>
`);
});
// Create and run an HTTP server which can handle incoming requests
const server = http.createServer(app);
server.listen(PORT, () =>
console.log(`Express server listening on localhost:${PORT}`)
);
// -----------------------------------------------------------------------------
// This code sets up a tool called ngrok to let Twilio talk to the app running
// on your computer. It then uses the Twilio REST API to direct all incoming
// SMS messages to your local app. You should not have to edit any of this
// code below.
// -----------------------------------------------------------------------------
(async function() {
try {
await ngrok.disconnect();
await ngrok.kill();
let url = await ngrok.connect(PORT);
console.log('ngrok forwarding configured - your app is live at ', url);
let client = twilio(
process.env.TQ_TWILIO_ACCOUNT_SID,
process.env.TQ_TWILIO_AUTH_TOKEN
);
let ngrokUrl = `${url}/sms`;
let number = await client
.incomingPhoneNumbers(process.env.TQ_TWILIO_NUMBER_SID)
.update({
smsUrl: ngrokUrl,
smsMethod: 'POST',
});
console.log(
`${number.phoneNumber} configured to send incoming SMS to ${ngrokUrl}`
);
console.log(
`Send a message to ${
number.phoneNumber
} and check the reply you get back!`
);
} catch (e) {
console.log('There was an error configuring incoming SMS:');
console.log(e);
}
})();
Response
Cannot GET /sms
Thanks

socket io client lost conection when server restarts

so I am creating a module for the members that are using my services (cloudlist.xyz).
basically, we have a voting system in our service, this module is making a connection using socket io on the server and socket io client on the module, announcing to the user when someone votes on it
Everything is working normally, but when I restart the server to do some maintenance, all users are disconnected from socket io even when the server is on again
Server side code :
var server = app.listen(process.env.PORT || 3000, () => {
console.log("Your app is listening on port " + server.address().port)
});
var io = require('socket.io')(server)
io.on("connection",function(socket) {
console.log("Someone Joined to our server api!")
})
//that's the part that he emits the event when someone votes
io.of(`vote/${bot}`).emit("voted", user_votes.val());
Module/client side:
var https = require('https');
const { EventEmitter } = require("events");
var fetch = require('node-fetch')
const io = require("socket.io-client");
module.exports = class Cloud_client extends EventEmitter {
constructor(id, token) {
super();
if (!id) throw new Error("Missing client instance on contructor");
if (!token) throw new Error("Missing token on constructor");
this.id = id;
this.token = token;
this.socket = io.connect(`https://www.cloudlist.xyz/vote/${id}`, {
reconnect:true,
autoConnect:true,
reconnectionDelay: 1000,
reconnectionDelayMax : 5000,
reconnectionAttempts: Infinity
});
this.socket.on("connect", () => this.emit("connected"));
this.socket.on("disconnect", (...args) => {this.socket.open();
});
this.socket.on("voted", (...args) => this.emit("voted", ...args));
};
this is an example of someone using the module:
var cdl = require("cloud-list")
var cloud_client = new cdl("701456902160121966","5669556617e2a070ada1688")
cloud_client.on("connected", (data) => {
console.log(`Connected to the api Server`)
})
cloud_client.on("voted", (data) => {
console.log(`Thanks,user ${data.user_name} for voting on us :)`)
})
When I connect to the server, it sends the message of this example saying "Connected to the api Server", but when I restart the server, I don't receive anything. Already tried this.socket.on("disconnect", (...args) => {this.socket.open()}); or this.socket.on("disconnect", (...args) => {this.socket.connect()}); ,but still the same thing,user can't reconnect again.
the only way for users to connect again is to restart his project, which is very bad
Socket connections require the server to be serving. Socket.io doesn't seem good for a voting system unless you want it to be real time. It's expected for clients to restart when the server restarts.
As per with working in Socket server we need to restart our node socket server during the restart of the main servers like apache or Nginx.
Because it is not an automatic process on the server.

expressJS: How to push socketIO on get call?

This is how I set up a simple expressJS server with a socket.IO connection.
My application is reading some sensor data every 10 seconds, which gets pushed to every client. This is no problem for me.
But also one client can call /set-status with some parameter to change some status data. In that case the new status data should be send to every client. That's why a simple request/response attempt is not working.
What do I have to do to push the socketIO connection after /set-status has been called?
const express = require('express')
const http = require('http')
const socketIo = require('socket.io')
const app = express()
const server = http.createServer(app)
const io = socketIo(server)
io.on('connection', socket => {
getStatus(socket)
getData(socket)
setInterval(
() => getData(socket),
10000
)
})
app.get('/set-status', (req, res) => {
// Change some data and push new data to every client
// How to get access to socket?
res.send(200, 'new-data')
})
const getStatus = async socket => {
const res = { initial: 'data' }
socket.emit('exampleStatus', res)
}
const getData = async socket => {
// read some sensor data, which is refreshed every 10 sec.
// this is working as expected
const res = { some: 'sensor data' }
socket.emit('sensorData', res)
}
server.listen(port, () => {
if (process.env.NODE_ENV !== 'production') {
console.log(`Listening on port ${port}`)
}
})
If client sockets are listening for exampleStatus events, you can emit an exampleStatus event from inside of your get callback. It would look like this: io.emit('exampleStatus', data). On your client sockets, you can write a listener which looks like socket.on('exampleStatus, data => // do something with data).

Categories