Socket.IO: "UnhandledPromiseRejectionWarning: Error: Callbacks are not supported when broadcasting" - node.js

im trying to create a login system with Node.js, Socket.IO and MongoDB.
At one point i have to get a certain cookie of the client.
So i "send" an event to the client which should return the cookie so i can work with that data within the same function.
My code is as follows:
Server:
async function checklogin(user) {
user = user;
console.log("user:", user);
await User.find({username:user}).then(function(docs) {
servercookieid = docs[0].cookieid;
servercookiedate = docs[0].cookiedate;
});
io.emit('getCookie', function(responseData) {
console.log(responseData)
}).catch(error)
}
Client:
socket.on('getCookie', function(callback) {
console.log('getting cookie...');
var Cookie = document.cookie;
callback(Cookie)
});
I really dont know why i get this error, because as you can see i am not even broadcasting, sooo...
/shrug
If you need more information, please dont hesitate to ask.

The error message says:
Socket.IO: “UnhandledPromiseRejectionWarning: Error: Callbacks are not
supported when broadcasting”
But in fact when you do io.emit you're broadcasting.
Currently you're broadcasting when you do the command:
io.emit('getCookie', function(responseData) {
console.log(responseData)
}).catch(error)
But for broadcasting you just need set a parameter, it's not a function.
Just fix your code to fix the error.
Changing to:
io.emit('getCookie', responseData);//must set the cookie
by the code you provided responseData is not defined. But by the name it should be a cookie.

Related

How to safely get the current user id in socket.io/Node.JS?

I am developing a simple API for a chat application on Node.js Express, and by assignment, it is required to make it possible to communicate between two users using socket.іо. I am faced with the problem that I cannot "safely" transfer information about the current user to the socket in any way. Information about the user with whom the correspondence is conducted can be specified in the socket parameters when connecting, which I do, but what about the current user (me)?
For example, I can do this:
const {receiverId, myId} = socket.handshake.query;
That is, specify both ids when connecting. But this is very unsafe because anyone who will join the socket can specify any id and write anything on behalf of someone else (for example, through Postman WebSockets).
Another option I was considering is making a post request in which a connection to the socket will be created using request.user.id and the request parameter. Then the post request will look like this:
router.post('/chat/:receiver', function (req,res){
const {receiver} = req.params
const socket = io.connect('/')
socket.emit('initMyUserId', {
myId: req.user,
});
})
But this option also did not work, because the file where this function is located and the file where the io variable is initialized are different, and I am not sure that it is generally advisable to transfer and use it in this way. Moreover, this approach will not allow me to check the operation of sockets via Postman, because the connection will occur in a post request, and not manually.
Are there working options to safely transfer the current user id with the ability to test it normally in postman? Or at least just safely pass the current user id if it doesn't work with Postman.
Here is the full code snippet for the socket events handlers:
module.exports = function(io) {
io.on('connection', (socket)=>{
const {id} = socket;
console.log(Socket connected: ${id});
const {idUser} = socket.handshake.query;
console.log(Socket idUser: ${idUser});
socket.on('message-to-user', (msg) => {
msg.type = user: ${idUser};
socket.to(idUser).emit('message-to-user', msg);
socket.emit('message-to-user', msg);
});
socket.on('disconnect', () => {
console.log(Socket disconnected: ${id});
});
});
}

Cannot set headers after they are sent to the client when adding data to firebase

I've been having this error
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:533:11)
After I added an update firebase code in a socket.io
io.on('connection', socket => {
...
socket.once('myEvent', async (dataToSend) => {
try {
await db
.collection('myCollection')
.doc('myDoc')
.update({keyToUpdate: dataToSend})
} catch (err) {
console.log(err)
}
}
}
I thought the problem was with socket so I did the same logic but with a POST route outside the socket, and got the same error, I tried adding a return but didn't work
I suspect it could be because this function is a promise and I'm not handling asynchronous behaviour well, or because the server is acting as a stream thanks to socket.io and this firebase function doesn't like that.
I have other firebase functions in the code but they are get requests (.onSnapshot()) the error only happens with data adding firebase functions (.set(), .add(), .update())
Would appreciate some help here, thanks.
Fixed, didn't exactly knew how onSnapshot works, so everytime an update was made it triggered its callback which was sending info and triggering the error.
Changed to .get().then() and that fixed it

How to get current socket object or id with in a sails controller?

I would like to access the currently connected socket id with in a sails.js (v0.12 ) controller function.
sails.sockets.getId(req.socket); is showing undefined since this is not a socket request
My objective is to set the online status of my user in the database when he logged in successfully
login: function (req, res) {
Util.login(req, function(){
var socketId = sails.sockets.getId(req.socket);
console.log('socketId ===', socketId); // return undefined
});
},
Basically i would like to access the current user's socket object in a controller or access current user's session object with in a socket on method
Also i'm not sure that how can i rewrite my old sockets.onConnect
handler
onConnect: function(session, socket) {
// Proceed only if the user is logged in
if (session.me) {
//console.log('test',session);
User.findOne({id: session.me}).exec(function(err, user) {
var socketId = sails.sockets.getId(socket);
user.status = 'online';
user.ip = socket.handshake.address;
user.save(function(err) {
// Publish this user creation event to every socket watching the User model via User.watch()
User.publishCreate(user, socket);
});
// Create the session.users hash if it doesn't exist already
session.users = session.users || {};
// Save this user in the session, indexed by their socket ID.
// This way we can look the user up by socket ID later.
session.users[socketId] = user;
// Persist the session
//session.save();
// Get updates about users being created
User.watch(socket);
// Send a message to the client with information about the new user
sails.sockets.broadcast(socketId, 'user', {
verb :'list',
data:session.users
});
});
}
},
You need to pass the req object to the method.
if (req.isSocket) {
let socketId = sails.sockets.getId(req);
sails.log('socket id: ' + socketId);
}
Since the request is not a socket request, you might need to do something like
Send back some identifier to the client once logged in.
Use the identifier to join a room. (One user per room. )
Broadcast messages to the room with the identifier whenever you need to send message to client.
https://gist.github.com/crtr0/2896891
Update:
From sails migration guide
The onConnect lifecycle callback has been deprecated. Instead, if you need to do something when a new socket is connected, send a request from the newly-connected client to do so. The purpose of onConnect was always for optimizing performance (eliminating the need to do this initial extra round-trip with the server), yet its use can lead to confusion and race conditions. If you desperately need to eliminate the server roundtrip, you can bind a handler directly on sails.io.on('connect', function (newlyConnectedSocket){}) in your bootstrap function (config/bootstrap.js). However, note that this is discouraged. Unless you're facing true production performance issues, you should use the strategy mentioned above for your "on connection" logic (i.e. send an initial request from the client after the socket connects). Socket requests are lightweight, so this doesn't add any tangible overhead to your application, and it will help make your code more predictable.
// in some controller
if (req.isSocket) {
let handshake = req.socket.manager.handshaken[sails.sockets.getId(req)];
if (handshake) {
session = handshake.session;
}
}

Node.js server side connection to Socket.io

I have a Node.js application with a frontend app and a backend app, the backend will manage the list and "push" an update to the frontend app, the call to the frontend app will trigger a list update so that all clients receive the correct list data.
The problem is on the backend side, when I press the button, I perform an AJAX call, and that AJAX call will perform the following code (trimmed some operations out of it):
Lists.findOne({_id: active_settings.active_id}, function(error, lists_result) {
var song_list = new Array();
for (i=0; i < lists_result.songs.length; i++) {
song_list.push(lists_result.songs[i].ref);
}
Song.find({
'_id': {$in: song_list}
}, function(error, songs){
// DO STUFF WITH THE SONGS
// UPDATE SETTINGS (code trimmed)
active_settings.save(function(error, updated_settings) {
list = {
settings: updated_settings,
};
var io = require('socket.io-client');
var socket = io.connect(config.app_url);
socket.on('connect', function () {
socket.emit('update_list', {key: config.socket_key});
});
response.json({
status: true,
list: list
});
response.end();
}
});
});
However the response.end never seems to work, the call keeps hanging, further more, the list doesn't always get refreshed so there is an issue with the socket.emit code. And the socket connection stays open I assume because the response isn't ended?
I have never done this server side before so any help would be much appreciated. (the active_settings etc exists)
I see some issues that might or might not be causing your problems:
list isn't properly scoped, since you don't prefix it with var; essentially, you're creating a global variable which might get overwritten when there are multiple requests being handled;
response.json() calls .end() itself; it doesn't hurt to call response.end() again yourself, but not necessary;
since you're not closing the socket(.io) connection anywhere, it will probably always stay open;
it sounds more appropriate to not set up a new socket.io connection for each request, but just once at your app startup and just re-use that;

How to get multiple socket attributes in socket.io?

I'm building a chat app on which, for every client connection, i need to set some attributes to every client instance using socket.io. When i save the attribute, i use:
client.set('name', name, function () {});
client.set('email', email, function () {});
....
and it runs fine.
When i need to get all the client properties, i have not found a better way than this:
client.get("name",function(err,name) {
client.get("email",function(err,email) {
.......
}
}
I need to nest all the "get" to asynchronously get data; but if i had 10 properties to get, do i need to nest all the 10 items? There must be a better way to do it, can anyone help me?
I don't attach attributes to the socket.
io.sockets.on('connection', function(socket) {
var username = "xx";
var email = "xx";
socket.on('doX', function(data) {
socket.emit('ackX', {username: username, email: email});
});
});
I don't know if it's the best solution, but I have seen many examples like that.
EDIT : socket.io - getting more than one field for a socket?
The correct answer may fit your needs

Resources