Socket.io - How to get client URL request on server side? - node.js

In my app.js (server) I need to know from where (URL) the request came.
Currently, I'm passing the URL as parameter from the client:
socket.emit('request', window.location.href);
and processing it server side
socket.on('request', function(url){
console.log(url);
...
});
But that's clearly risky and unsecure (clients can send anything to the server).
So I'm guessing.. is it possible to get the URL parameter only on server side? Maybe from the socket object?

For the entire URL you could use socket.handshake.headers.referer
io.on('connect', (socket) => {
console.log(socket.handshake.headers.referer);
});

To obtain the connection URL
io.sockets.on('connection', function(socket) {
console.log("url: " + socket.handshake.url);
});
This will return something like: url: /socket.io/1/?t=1407807394827

socket.request.headers.origin
This will return domain connection

Related

socketio + sails: does not receive response in socket frame

I created a socket server with sails. I use socket.io.js and sails.io.js to test, but the results are not the same
I couldn't find the reason why.
I want to fix socket.io to work the same as sails.io, for clients not using the sails framework
In my sample,
I created ChatController in sails server
/**
* ChatController
*/
module.exports = {
subcriber: function (req, res) {
return res.send('done');
},
};
In client code, I try socket.io and sails.io in turn. Both were able to call the function (by see the log on the server), but the client using socket.io did not receive the response like sails.io
client use sails.io
io.socket.get('/chat/subcriber', function responseFromServer(body, response) {
console.log("The server responded with status " + response.statusCode + " and said: ", body);
});
and client use socket.io
<script src="/socket.io/socket.io.js"></script>
var socket = io('http://localhost:1337/', {
transports: ['websocket'],
query: SDK_INFO.versionString
});
socket.emit('get', { method: "get", headers: {}, data: {}, url: "/chat/subcriber" });
This is results
client use socket.io https://prnt.sc/n2ya3w
client use sails.io https://prnt.sc/n2y9yz
I would like for the client to use socket.io can also get a response like the client use sails.io
Can anyone give me experience on this issue?
Thanks

Why does Socket.io server override node http middleware?

I have made a React application which relies fully on WebSockets after the initial HTTP Upgrade. For security reasons i use a cookie AND a JWT token in my WebSockets connection.
It all works fine, but when opening a new tab, socket.io cookies get reissued and I want users to stay logged in over multiple tabs. So i want to set a cookie if the client doesn't already have one. If it already has one, then use that cookie.
So I want to handle the first HTTP polling requests and created middleware for that in Node's http server:
// HTTP SERVER
const server = require('http').createServer(function (request, response) {
console.log('test');
console.log(request);
if(!request.headers.cookie) { // cookie pseudo-logic
response.writeHead(200, {
'Set-Cookie': 'mycookie=test',
'Content-Type': 'text/plain'
});
}
// Socket.IO server instance
const io = require('socket.io')(server, {
origins: config.allowedOrigins,
cookie: false, // disable default io cookie
});
server.listen(port, () => console.log(`Listening on port ${port}`));
I use Socket.io as WebSockets framework. The problem however is that this middleware get's ignored, when registering the Socket.io server. When i comment out the Socket.io server, the middleware is active and the request get's logged.
It looks like Socket.io's server is overriding the handler for node http's server. In the Socket.io docs however they provide this example:
var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');
app.listen(80);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
Thus indicating that it should be possible to handle thw first http polling requests and also the socket requests. I managed to get it work with Express, but I don't understand why node's http server can't.
Anybody who knows what's happening?
Thanks in advance,
Mike
Because normal usage of socket.io does not want regular http middleware to see socket.io connection requests (they would normally trigger 404 responses), socket.io places its own request handler first in line before any others, even ones that existed before it was installed.
You can see how it does that here: https://github.com/socketio/engine.io/blob/master/lib/server.js#L437 in the engine.io source.
I can think of the following ways for you to pre-process a request before socket.io sees it:
Use a proxy and do your cookie stuff in a proxy before socket.io even sees the request.
Patch socket.io/engine.io code to add a callback hook for what you want to do.
Copy the technique used by socket.io/engine.io to put your own request handler first in line after socket.io is configured.
Find a way to override the socket.io server object's handleRequest() method which is what gets called when there's an incoming connection request. You can see its code here.

node.js + socket.io authetificate right away

Is it possible to run authentification as soon as socket is connected?
Right now I do this:
io.sockets.on('connection', function (socket) {
socket.on('login', function (token) {
// this is where I currently authtificate users
});
});
If I'm not wrong in this case socket opens on "connection" and hangs there and "login" event may be never called. If someone wants to attack my server, they can simply open thousands of socket connections. How can I authentificate on "connection", I mean right away? So if it fails I will be able to close socket immediately.
Thank you!
It is possible to run authentication when a client tries to connect. Socket.io has a mechanism of authorizing clients when they try to connect to your server, However authorization is disabled by default. There is an object of handshake when a socket tries to connect to server, this object has the request query and request headers etc. You can enforce authorization by this:
io.configure(function (){
io.set('authorization', function (handshakeData, callback) {
if(handshakeData.query.token == "somevalue"){
callback(null, true); // this will allow the client to connect
}else{
callback(null, false); // this will prevent the client from connecting
}
});
});
And on the client side you can send a query string like this:
var socket = io.connect("http://yourIP:Port?token=somevalue");

Nodejs express headers on the websocket scope

I've got a node.js + express + socket.io app.
i want to save the request headers in the socket, to use later.
io.sockets.on('connection', function (socket) {
socket.headers = {};
app.get('*', function (req, res) {
//fetch headers
socket.headers.ua = req.headers['user-agent'];
res.sendfile(__dirname + '/index.html');
});
....etc
but because i am in the app scope, socket isnt defined. I always get confused with the in and out scope.
i cannot app.get() it, because if another browser connects, the app will be changed, right?
You're doing it wrong. Every socket has a handshake object with it which contains request headers, domain, host, date etc. If you still want to fetch headers information then do this:
io.sockets.on('connection', function(socket) {
console.log(socket.handshake); //This would print handshake object in JSON format
// to get req-header, do this
socket.head = socket.handshake.headers['user-agent'];
});
And you can use this property later in some event like:
socket.on('EventName',function(data){
console.log(socket.head);
});

How do I connect two Socket.io servers/apps?

I'm trying to build an application that has two components. There's a public-facing component and an administrative component. Each component will be hosted on a different server, but the two will access the same database. I need to set up the administrative component to be able to send a message to the public-facing component to query the database and send the information to all the public clients.
What I can't figure out is how to set up a connection between the two components. I'm using the standard HTTP server setup provided by Socket.io.
In each server:
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
app.listen(80);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
And on each client:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
I've looked at this question but couldn't really follow the answers provided, and I think the situation is somewhat different. I just need one of the servers to be able to send a message to the other server, and still send/receive messages to/from its own set of clients.
I'm brand new to Node (and thus, Socket), so some explanation would be incredibly helpful.
The easiest thing I could find to do is simply create a client connection between the servers using socket.io-client. In my situation, the admin server connects to the client server:
var client = require("socket.io-client");
var socket = client.connect("other_server_hostname");
Actions on the admin side can then send messages to the admin server, and the admin server can use this client connection to forward information to the client server.
On the client server, I created an on 'adminMessage' function and check for some other information to verify where the message came from like so:
io.sockets.on('connection', function (socket) {
socket.on('adminMessage', function (data) {
if(data.someIdentifyingData == "data") {
// DO STUFF
}
});
});
I had the same problem, but instead to use socket.io-client I decided to use a more simple approach (at least for me) using redis pub/sub, the result is pretty simple. My main problem with socket.io-client is that you'll need to know server hosts around you and connect to each one to send messages.
You can take a look at my solution here: https://github.com/alissonperez/scalable-socket-io-server
With this solution you can have how much process/servers you want (using auto-scaling solution), you just use redis as a way to forward your messages between your servers.

Resources