hapijs with socket.io the right way? - node.js

I'm trying to use hapijs with socket.io and already searched a lot about how to integrate socket.io into the hapi server the right way. One approach, I found, is this example and I'm still not sure if this is the way to go. I have to admit that I'm new to hapijs and I'm still learning it :)
var Path = require('path');
var Hapi = require('hapi');
var socketio = require('socket.io');
var server = new Hapi.Server();
server.connection({port: 3000});
server.register(require('vision'), function (err) {
server.views({
engines: {
ejs: require('ejs')
},
relativeTo: __dirname,
path: 'templates'
});
});
var plugins = [
{register: require('./lib/index.js')}
];
server.register(plugins, function (err) {
server.start(function () {
io = socketio.listen(server.listener);
io.on('connection', function (socket) {
socket.on('create', function (room) {
socket.join(room);
console.log("Joined room: " + room);
socket.emit('message', "Joined room");
});
});
console.log('Server running at:', server.info.uri);
});
});
And the lib/index.js
exports.register = function(server, options, next) {
var tasks = [];
server.route([
{
method: 'GET',
path: '/tasks',
handler: function (request, reply) {
reply.view('index', { sid: "6001" });
io.emit('message', "Test");
}
},
{
method: 'POST',
path: '/tasks/{name}',
handler: function (request, reply) {
reply.view('index', { sid: "6001" });
io.emit('message', "Test");
}
},
{
method: 'POST',
path: '/tasks',
handler: function (request, reply) {
io.emit('message', "Test");
}
}
]);
next();
}
exports.register.attributes = {
name: 'routes-tasks',
version: '1.0.0'
};
Please correct me if this is not the way how to use hapijs.

you are near to it.
1)on disconnect close the sockets
socket.disconnect('unauthorized'); or socket.close();
2)use auth for every subscription
{
method: 'POST',
path: '/tasks/{name}',
auth: auth,
handler: function (request, reply) {
reply.view('index', { sid: "6001" });
io.emit('message', "Test");
}
},
3)must important thing is use Access-Control-Allow-Origin otherwise websocket hijacking will happen.
request.response.header('Access-Control-Allow-Origin', 'your domain')
if you need further info let me know.I am happy to help

Related

Laravel chat app : Unable to deliver messages to a specific user

I have been trying to develop a chat app on Laravel with socket.io. Now, I am facing problem that is when a user is sending a message to a specific user, the message is being delivered to all the available users. May I know the section of code you need to help me out? Or there's some other area where I can specifically look into.
const express = require("express");
const app = express();
const server = require('http').createServer(app);
const io = require("socket.io")(server, {
cors: { origin: "*" }
});
server.listen(3000, () => {
console.log('Server is running');
io.on("connection", function(socket) {
console.log("User" + socket.id);
socket.on("messageSent", function(message, senderId) {
socket.broadcast.emit("messageSent", message, this.socket.id)
console.log(this.socket.id);
});
//msg
socket.on("msgSent", function(message) {
socket.broadcast.emit("msgSent", message)
});
socket.on("clientMmsgSent", function(message) {
socket.broadcast.emit("clientMmsgSent", message)
});
});
});
Other code:
<sc ript>
var socket = io("{{config('app.server_url')}}");
function sendMessage(event) {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
//console.log('test');
event.preventDefault();
if (event.keyCode === 13) {
var msg = document.getElementById('msg').value;
var client_id = document.getElementById('client_id').value;
var operator_id = document.getElementById('operator_id').value;
//console.log(smg);
$.ajax({
type: "POST",
url: "{{route('send.msg')}}",
data: {
msg: msg,
client_id: client_id,
operator_id: operator_id,
},
beforeSend: function() {
},
success: function(data) {
var msg = `
<div class="operator-msg">
${data.operator_msg}
</div>
`;
console.log(data);
socket.emit("msgSent", {
'data': data,
});
$('#opt_msg').append(msg);
},
error: function(error) {
console.log(error);
}
});
$('#msg').val(" ");
return true;
} else {
return false;
}
}
</sc ript>

Server Sent Event; `EventSource.onmessage` not firing

I'm trying to use the following example of Server Sent Events. Seems like the server is emitting the data, but the event is not firing and no data is recorded in the event stream (through the developer tools).
Angular code (service):
getAlertsEvent(fieldIds: Guid[]): Observable<responseModel.LoanAlert> {
return new Observable(obs => {
fieldIds.forEach(fieldId => {
const source = new EventSource(`http://localhost:3000/loan/alerts/${fieldId}`);
alert('Succesfully creates the EventSource, I see reuslt 200 in Networking tab but with 0 events');
source.onmessage = (ev): void => {
this.zone.run(() => {
alert('This alert will not happen');
obs.next(ev.data);
});
};
source.onerror = (err): void => this.zone.run(() => obs.error(err));
// Also tried this way with no luck:
// source.addEventListener('message', (event) => {
// obs.next(event.data);
// });
});
});
}
Component:
this.loansService.getAlertsEvent(this.fields.map(field => field.fieldId)).subscribe(alert => {
console.log(alert);
});
Node.js code:
const express = require('express');
const parser = require('body-parser');
const app = express();
const EventEmitter = require('events');
const Stream = new EventEmitter();
app.unsubscribe(parser.json());
app.use(
parser.urlencoded({
extended: true,
})
);
app.get('/loan/alerts/:fieldId', function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Access-Control-Allow-Origin': "*",
Conection: 'keep-alive'
});
Stream.on(req.params.fieldId, function(event, data) {
res.write(JSON.stringify(data));
});
});
setInterval(function() {
const item = {
formId: 51415,
alertId: 'asdfasdfasdf',
messageStatus: 'NEW',
messageType: 'ACTION_MESSAGE_FROM_SERVER',
actionType: 'NAVIGATION',
message: 'New Message!',
archiverId: '12345',
documentCode: 3,
from: 'Internal Server Message',
messageTimestamp: new Date().toJSON(),
markedAsRead: false,
};
Stream.emit('aed29580-09fd-e411-b8e1-e61f13cf5d4b', 'message', item);
}, 5000);
app.listen(3000);
console.log('Express E2e Mock server is running');
When manually going to http://localhost:3000/loan/alerts/aed29580-09fd-e411-b8e1-e61f13cf5d4b I'm seeing the messages printed to the screen, so I guess that this is either an Angular or a missing security header.
Thanks!
I just realized, thanks to this answer, that events must be formatted in a specific way. I changed the value of res.write accordingly:
Stream.on(req.params.fieldId, function(event, data) {
res.write('event: ' + String(event) + '\n' + 'data: ' + JSON.stringify(data) + '\n\n');
});

How to implement oauth2orize in hapijs

I implemented oauth2orize in hapijs. But when I am calling the api, nothing happen. The function goes inside code.js file of oauth2orize module and hangs in between. Please suggest me how to implement oauth2orize in hapjs. hapi-oauth2orize is also not working as immigration & hapi-oauth2orize plugin throws option error.
const Hapi = require('hapi');
const server = new Hapi.Server();
const oauth2orize = require('oauth2orize');
var oauth = oauth2orize.createServer();
server.connection({
host: 'localhost',
port: 8000
});
server.register([{
register: require('hapi-mongodb'),
options: dbOpts
}], function (err) {
if (err) {
console.error(err);
throw err;
}
server.start();
server.route([
{
method: 'GET',
path: '/oauth/authorizegrant',
config: {
auth: false,
handler: function(request, reply) {
var clientId = request.query.client_id,
redirectUrl = request.query.redirect_uri,
resType = request.query.response_type,
state = request.query.state;
oauth.grant(oauth2orize.grant.code(function(clientId,redirectUrl,resType,state,callback) {
// Create a new authorization code
console.log('client', client);
var db = request.server.plugins['hapi-mongodb'].db;
var code = new Code({
value: uid(16),
clientId: client._id,
redirectUri: redirectUri,
userId: user._id
});
// Save the auth code and check for errors
db.collection('codes').insert(code, function(err) {
if (err) { console.log('err*********', err); return callback(err); }
callback(null, code.value);
});
}));
}
}
},
]);
});
You need to change parameters passed to oauth.grant function, the callback should be removed and replaced by hapi's reply function. A simple snippet would be
if (err) {
return reply(err);
}
return reply(code.value);
I would file an issue in the plugin repo as this is the best way to interface between hapi and oauth2orize.

Blocking requests coming from host A to host B nodejs

My nodejs httpserver (i'm not using express) is hosted in HOST A, domain: www.host-a.com and does this:
dispatcher.addListener("post", "/admin/insert_data", function(req, res) {
var body='';
req.on('data', function(chunk) {
body += chunk.toString();
});
req.on('end', function() {
var parsedbody = require('querystring').parse(body);
MongoClient.connect('mongodb://localhost:27017/database1', function(err, db) {
if (err) {
res.writeHead(500) ;
return res.end('Database offline') ;
}
console.log("Connected correctly to server");
var col = db.collection('mycollection');
col.insert(parsedbody, function() {
db.close();
var json = JSON.stringify({status: "0"});
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(json);
});
});
});
});
The client side is the following:
$("form[name='manage_notizie']").submit(function(e) {
req="../admin/insert_data"
var tmp_notizia = $( "input[name=notizia]" ).val();
var tmp_id_notizia = $( "input[name=id_notizia]" ).val();
$.ajax({
url: req,
type: "POST",
data: {id_notizia:tmp_id_notizia, notizia:tmp_notizia},
async: false,
success: function (msg) {
location.reload();
},
error: function (msg) {
alert("Errore nel server")
},
cache: false,
});
e.preventDefault();
});
I know that by deafult, if I don't specify any access control allow origin, the server will respond only if the request arrives from itself (host a).
Now, for example if a request comes from www.host-b.com to www.host-a.com/insert_data, my server would not answer to the request (like I want) but it does the computing stuffs (which I don't want)
Am I missing something?

io.of('namespace').emit('event', message) not working with namespace in socket.io

I have an app like this following:
io.of('/hello').on('connection', function(socket) {
socket.emit('world', {});
});
app.post('/', function *(next) {
console.log("At here......");
var pushMessage = (yield parse.json(this));
console.log(pushMessage);
if(flag !== 0) {
// io.of('/hello/').emit('world', pushMessage);
io.sockets.emit('world', pushMessage);
} else {
console.log("Do Nothing");
}
});
It receive a http request and emit an event. When I use io.sockets.emit it works well but when I specify a namespace with 'io.of('hello').emit' it doesn't work,why?
My client side is this:
var socket = io.connect('http://localhost:3000', {
'reconnection delay': 100,
'reconnection limit': 100,
'max reconnection attempts': 10
});
//server side use io.sockets.emit
socket.on('world', function(data) {
alert(data.a);
});
//if server side use io.of('/hello/').emit
//socket.of('/hello/').on('world', function(data) {
// alert(data.a);
//});
Your code is more or less fine, but you are on different namespaces.
io.sockets.emit() broadcasts to everybody currently connected to your server via socket. That's the reason it works. Technically it's because that's a 'shortcut' for io.of('').emit() ('' being the namespace).
Assuming you're going to use the /hello namespace, this is what you have to do on your client:
var socket = io.connect('http://localhost:3000/hello'); // your namespace is /hello
on the server you first have to listen for connections on that namespace:
io.of('/hello').on('connection', function(socket) {
socket.emit('world', { a: 'hi world' });
});
then:
io.of('/hello').emit('something');
You may want to look at these: socket.io: How to use and socket.io rooms on GitHub
### UPDATE ###
I conducted a little test:
client:
$('document').ready(function() {
var socket = io.connect("localhost:3000/hello");
socket.on('hello', function() {
console.log('hello received');
});
var data = {};
data.title = "title";
data.message = "message";
setTimeout(function() {
$.ajax({
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json',
url: 'http://localhost:3000/hello',
success: function(data) {
console.log('success');
console.log(JSON.stringify(data));
}
});
}, 2000);
});
server:
io.of('/hello').on('connection', function() {
console.log("client connected");
});
app.post('/hello', function(req, res) {
io.of('/hello').emit('hello');
});
... and it worked. I copied the jquery-ajax code from here.

Resources