socket.io query parameter not working - javascript

I have this code:
var ss = io('/secure', { query: 'token='+string });
It used to pass the query parameter to the server that was accessible as socket.handshake.query with this code:
io.of('/secure').use(function(socket, next){
// ***
console.log( socket.handshake.query );
// validate token
next();
});
but now it is not working and the output of the console.log does not include the token property:
{ EIO: '3', transport: 'polling', t: '1410465157747-0' }
I also tried changing my syntax to syntax that I have seen elsewhere online...
var ss = io('/secure', { query: { 'token': string } });
and this does not add the query property to socket.handshake.query either...
Please help!
btw I am using socket.io 1.1.0

I am on socket.io latest version (v 1.3.6) and found the same issue - for second connection, the query is not being sent.
I use socket.io for two purposes, chat and notification. Both were running on same port. Since these were two separate services, I started two separate servers on different ports - one for each service. Since then the issue has not been seen.
Try this: So if you have more than one service, try separating them into different ports.
var http_nfy = require('http').Server(app).listen(6001);
var io_nfy = require('socket.io')(http_nfy);
var http_cht = require('http').Server(app).listen(6002);
var io_cht = require('socket.io')(http_cht);
Then I used these two io differently:
io_nfy.emit(channel + ':' + message.event, message.data);
var chatNsp = io_cht.of('/chat');
I am not sure if this is a bug or intended behaviour, hence did not open an issue. If someone can confirm that this is a bug, I would be happy to report and keep track of that bug.

Related

How to get the socket id when parsing through io.sockets.sockets

I'm using Socket.io on a Node.js server.
for (connectedSocket of io.sockets.sockets) {
console.log(`TEST: id = ${connectedSocket.id}`) //Why is this 'undefined'? All I want is the unique identifier of each socket in the server.
}
Not sure how to fix this, but it has to be super simple. Sorry about the fail code formatting :o
As of socket.io v3, io.sockets is now a Map object as shown here. You can iterate it directly with:
for (let [id, socket] of io.sockets.entries()) {
// can use socket here
}
You can use the newer interface:
let socketsArray = await io.fetchSockets();
to get you an array of connected sockets.
Note, you can also use fetchSockets() with rooms and namespaces as shown here.

Is there some way of importing a variable(specifically map) from the server side onto the client side(Socket.io)?

I am trying to assign unique colors to each different client( by using socket.id ). In my map() I have paired (socket.id,randomcolor()), but this variable is on the server side. I've found out that the require() statement doesn't work on client side,
why is that and what is a solution to it? I want to be able to pass map() variable to the client side so that it uses the color assigned to that socket.id and displays the color accordingly.
Or is there some way to know the socket.id on the client side(I don't think it is but not sure), specifically a users computer has to know who sent the message i.e. what socket.id was used to send the message, Is it possible to know that?
Here's my server side:
var express = require('express');
var app = express();
app.use(express.static('public'))
var http = require('http').createServer(app);
var io = require('socket.io')(http);
const map = new Map();
io.on('connection', function(socket) {
console.log('connected by ' + socket.id);
map.set(socket.id, RandomColor())
socket.on('chat', function(data) {
//emitting to all sockets connected
io.emit('chat', data);
console.log(map.entries());
});
socket.on('typing', function(data) {
socket.broadcast.emit('typing', data);
})
});
http.listen(3000, function() {
console.log('listening on port 3000');
});
Here's client side :
// import '../index';
var socket = io.connect('http://localhost:3000')
var message = document.getElementById('Message');
var handle = document.getElementById('Handle');
var btn = document.getElementById('Send');
var output = document.getElementById('Output');
var feedback = document.getElementById('Feedback');
var ids = []
console.log(server);
//emit event
btn.addEventListener('click', function() {
socket.emit('chat', {
message: message.value,
handle: handle.value,
})
})
message.addEventListener('keypress', function() {
socket.emit('typing', handle.value)
})
messageArray = []
//listening for any message received
socket.on('chat', function(data) {
// console.log(data);
feedback.innerHTML = ""
var item = document.createElement('li')
item.innerHTML = "<span style=\"font-family:\"cursive\";\" ;><strong>" + data.handle + ": " + data.message + "</strong></span>";
document.getElementById('Output').appendChild(item)
})
//listening for any typing event listener
socket.on('typing', function(data) {
feedback.innerHTML = "<p><strong>" + data + " is typing a message </strong></p>";
})
PS: Also, I'm new to JS and Socket.io so please suggest some good practices for anything in the code.
First of all, JS has no built-in include/reference property.
So you can't just join another file into another file. But some libraries achieve this with their own written methods etc.
A JS executed on the client-side is not able to access local files. Although you may access an online file load into the document or to an object. So similar functionality can be achieved via 3rd party scripts.
Node.JS follows the CommonJS module system and uses the power of being able to access the local file system.
About the index: So you don't need a Map and Map is pretty similar to a standard object, main difference is might be the order of contents.
But since all you need is a dictionary object. Just create a simple object. Then you can emit the color index whenever you want.
const colorIndex = {}
colorIndex[socketID] = color
Each can set their color on client-side and send it to the server, on each update server has to update every other client about the color.
A client cannot know other clients otherwise wouldn't be secure and it doesn't work like that. It works more like you are calling someone and the server is a middle man that connecting you two.
So, create an object, store socket ids, nicknames, any other info you need. Keep it on serverside, on each message send all of them together with the message.
const users = {}
io.on('connection', function(socket) {
users[socket.id] = {//Add new user
color:RandomColor()
}
socket.on('chat', function(message) {
let u = users[socket.id];//Get user from index
let data = {//Create a message package
user:(u.username)?u.username:"Guest", //name of the user if set
color:u.color,//color of user
message
}
io.emit('chat', data );//Send
});
socket.on('setColor', function(color) {//User can update color
users[socket.id].color = color
});
socket.on('setname', function(name) {//User can update username
users[socket.id].username = name
});
});
So you probably get the idea. There are bunch of ways to achieve.
I don't think you could send that map as an argument, but you can't try creating an array of arrays and emit it to an event like io.emit(colors, array) and once you have it on the client side you can transform back to a map using something like map or reduce
RequireJS is responsible to handle dependencies and ensure that you have everything you need. It is a Javascript library which can work anywhere you use Javascript at, including your server and client-side. The reason it does not work on your client-side (which manifests in the error you see) is that it's not configured on your client-side.
You can read about configurating RequireJS as well.
However, if you set it up properly on your client-side, then there might still be issues, particularly if you try to use on your client-side something which is available on the server. Client-side is a browser, potentially very far from the server. Luckily there is a client API for Socket.IO.
EDIT
Server and client-side can share values in several ways:
WebSockets (a duplex protocol which should be chosen if available in most cases)
Push notifications
AJAX
Page load
Forever frame (that's a hack which should be avoided)

How to properly generate Facebook Graph API App Secret Proof in Javascript

I am making a server side Facebook Graph API call to the all_mutual_friends edge: https://developers.facebook.com/docs/graph-api/reference/user-context/all_mutual_friends/
The call works when the two users are friends, but returns no useful data when they users aren't friends. According to the docs, this is because I must sign the call with the appsecret_proof parameter. No matter what I try, I am not able to successfully pass this parameter. I am using jsrsasign running on Parse. I have tried every configuration of using the access token as the message and my appSecret as the key, and vice versa. I have also tried multiple combinations of utf8 and hex. Every time I receive the error: invalid appsecret_proof provided in the API argument
Code:
var Signer = require("cloud/vendor/jsrsasign/lib/jsrsasign.js");
var userId = request.params.userId;
var accessToken = request.params.accessToken;
var appSecret = "redactedStringPastedFromFacebook";
var signer = new Signer.Mac({alg: "hmacsha256", pass: appSecret});
var appSecretString = signer.doFinalString(accessToken);
var appSecretHex = signer.doFinalHex(accessToken);
var graphRequestURL = "https://graph.facebook.com/v2.5/" + userId;
var fields = "?fields=context.fields(all_mutual_friends.fields(name,picture.width(200).height(200)))";
//var authorization = "&access_token=" + accessToken; //this works, but only for existing friends
var authorization = "&access_token=" + accessToken + "&appsecret_proof=" + appSecretHex;
return Parse.Cloud.httpRequest({
url: graphRequestURL + fields + authorization,
method: "GET",
})
Most examples I have seen are in PHP or Python and the crypto routines are a bit more clear. This works in that both appSecretString and appSecretHex don't throw errors and look reasonable, however the values are always rejected by Facebook.
Notes:
I have triple checked the App Secret value provided by Facebook
I have been approved by Facebook to use the all_mutual_friends feature, which is a requirement for this particular call
I am using Parse, which isn't Node, and can't use NPM modules that have external dependencies, which is why I am using jsrsasign. I also tried using CryptoJS directly, but it is no longer maintained and doesn't have proper module support and jsrsasign seems to wrap it anyway.
Here it is:
import CryptoJS from 'crypto-js';
const accessToken = <your_page_access_token>;
const clientSecret = <your_app_client_secret>;
const appsecretProof = CryptoJS.HmacSHA256(accessToken, clientSecret).toString(CryptoJS.enc.Hex);

A strange feature of node.js

Noticed a strange feature of node.js.
For example:
Let's say, I've got some variable on node.js server: var user_id = '1';, in which the stored user id which is connected to the server.
user1 have var user = 1;
user2 have var user = 2;
...
user99 have var user = 99;
But if at some point I will demand from the server variable user - I will return the id of the last user who rewrote her.
Is that right? So it should be?
I thought, node.js for each user creates a flow/process ...
Thanks for your answer!
index.js
dNode({
connect: function(data, callback) {
IM.iUserId = data.user_id;
IM.start();
}
});
im.js
var IM = {
iUserId: false,
start: function() {
console.log(this.iUserId);
}
};
It seems like you have one global IM objects that all of your connections are sharing.
You can set/get a value for each socket by using socket.set and socket.get
Node.js is single thread one context, it does not create any isolated context for users like in PHP.
Everything inside is shared and cross-acessible.

Node.js and zmq

I have a strange issue with a basic pubsub application with node and zmq:
a client is publishing strings to a broker, the problem is that the broker only receives the first line. At network level I've noticed that only the first message is sent then the next calls to .send() function have no effect (no packets are sent) so I suppose the problem is in the client/publisher.
I used the example code provided in the official guide which works perfectly, the only difference in my code is that I use prototype to have a reusable structure.
(I didn't paste subscriber's code because is not relevant and took some other not relevant stuff out)
relevant part of the client/publisher:
Publisher = function(zmq, pport) {
this.logread = spawn('tail', ['-n0', '-f', '/var/log/auth.log']);
this.publisher = zmq.socket('req');
this.pport = pport;
};
Publisher.prototype.start = function() {
var self = this;
this.publisher.connect('tcp://127.0.0.1:' + this.pport);
this.logread.stdout.on('data', function(data){
self.publisher.send(data.toString());
console.log(data.toString());
});
};
relevant part of the broker:
Broker = function(zmq, bpport, bsport) {
this.server = zmq.socket('rep');
this.bpport = bpport;
this.bsport = bsport;
};
Broker.prototype.start = function() {
this.server.on('message', function(request) {
console.log(request.toString());
});
this.server.bind('tcp://127.0.0.1:' + this.bsport, function(err) {
if (err)
console.log(err);
});
};
You are talking about publish subscribe pattern, but in your code, you create a req socket, and in the broker a rep socket, which is for the request-reply pattern. The request-reply pattern is strictly need to send first, than receive, see the api docs docs, or read more from the guide
I suppose you should use pub socket on the client side, and a sub socket on the other side, but don't know what do you want to achieve, maybe a different pattern would fit your needs better.
so I'll answer my question:
the server must send a reply to the client, until then the client will not send more messages
server.send('OK');
I also suppose there is a different way to achieve this

Categories