I am trying to add functionality to my error handler by not only logging the message to the console, but by redirecting the client's browser to a static HTML page that would display some simple text content. Here is the existing handler:
var sql = require('msnodesql');
//store a connection to MS SQL Server-----------------------------------------------------------------------------------
sql.open(connStr, function(err, sqlconn){
if(err){
console.error("Could not connect to sql: ", err);
}
else
conn = sqlconn; //save the sql connection globally for all client's to use
});
I'm using express.js to create my web server. This is server side code. I want this to happen in realtime, as soon as the error occurs the client's web browser gets redirected.
EDIT: I guess what I really want to know is how to redirect the client's browser to a page from inside if(err).
You can use a middleware that checks the state of the connection for each request and renders an appropriate template depending on the state (instead of using res.render, you can also use res.redirect or res.sendfile, of course):
var sql = require('msnodesql');
var conn = null;
sql.open(connStr, function(err, sqlconn) {
if (err) {
console.error("Could not connect to sql: ", err);
conn = false;
} else {
conn = sqlconn;
}
});
// Express middleware that checks the connection state of the database
// connection: active, not yet active, or failed.
app.use(function(req, res, next) {
// database connection not active yet
if (conn === null || conn === undefined) {
res.status(503);
return res.render('not-active-yet');
}
// database connection failed
if (conn === false) {
res.status(500);
return res.render('db-connection-failed');
}
// everything seems okay
next();
});
EDIT: forgot to mention that you need to include this middleware very early in the middleware chain, but certainly before any of your routes.
Related
So I have an application running node js with socket.io as a backend and normal javascript as frontend. My application has a login system which currently simply has the client send its login data as soon as it's connected.
Now I figured it would be much nicer to have the login data sent along with the handshakeData, so I can directly have the user logged in while connecting (instead of after establishing a connection) respectively refuse authorization when the login data is invalid.
I'm thinking it would be best to put my additional data in the header part of the handshakeData, so any ideas how I could do that? (Without having to modify socket.io if possible, but if it's the only way I can live with it)
As a lot of comments have pointed out below the Socket.IO API changed in their 1.0 release. Authentication should now be done via a middleware function, see 'Authentication differences' # http://socket.io/docs/migrating-from-0-9/#authentication-differences. I'll include my orginal answer for anyone stuck on <1.0 as the old docs seem to be gone.
1.0 and later:
Client Side:
//The query member of the options object is passed to the server on connection and parsed as a CGI style Querystring.
var socket = io("http://127.0.0.1:3000/", { query: "foo=bar" });
Server Side:
io.use(function(socket, next){
console.log("Query: ", socket.handshake.query);
// return the result of next() to accept the connection.
if (socket.handshake.query.foo == "bar") {
return next();
}
// call next() with an Error if you need to reject the connection.
next(new Error('Authentication error'));
});
Pre 1.0
You can pass a query: param in the second argument to connect() on the client side which will be available on the server in the authorization method.
I've just been testing it. On the client I have:
var c = io.connect('http://127.0.0.1:3000/', { query: "foo=bar" });
On the server:
io.set('authorization', function (handshakeData, cb) {
console.log('Auth: ', handshakeData.query);
cb(null, true);
});
The output on the server then looked like:
:!node node_app/main.js
info - socket.io started
Auth: { foo: 'bar', t: '1355859917678' }
Update
3.x and later
You can pass an authentication payload using the auth param as the second argument to connect() in the client side.
Client Side:
io.connect("http://127.0.0.1:3000/", {
auth: {
token: "AuthToken",
},
}),
In server side you can access it using socket.handshake.auth.token
Server Side:
io.use(function(socket, next){
console.log(socket.handshake.auth.token)
next()
});
This has now been changed in v1.0.0. See the migration docs
basically,
io.set('authorization', function (handshakeData, callback) {
// make sure the handshake data looks good
callback(null, true); // error first, 'authorized' boolean second
});
becomes :
io.use(function(socket, next) {
var handshakeData = socket.request;
// make sure the handshake data looks good as before
// if error do this:
// next(new Error('not authorized');
// else just call next
next();
});
For socket.io v1.2.1 use this:
io.use(function (socket, next) {
var handshake = socket.handshake;
console.log(handshake.query);
next();
});
This my code for sending query data to nodejs and server.io server client.
var socket = io.connect(window.location.origin, { query: 'loggeduser=user1' });
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.user);
}
Perhaps the api has changed but I did the following to get extra info to the server.
// client
io.connect('localhost:8080', { query: 'foo=bar', extra: 'extra'});
// server
io.use(function(sock, next) {
var handshakeData = sock.request;
console.log('_query:', handshakeData._query);
console.log('extra:', handshakeData.extra);
next();
});
prints
_query: { foo: 'bar',
EIO: '3',
transport: 'polling',
t: '1424932455409-0' }
extra: undefined
If anyone knows how to get data from a client to the server through the handshake that is not in the query params let me know please.
Update I ran into issues later with this syntax
io.connect('localhost:8080?foo=bar');
is what I'm currently using.
Old thread but assuming you store your jwt token/session id in session cookies (standard stuff) this gets passed to the server by default anyway when doing handshake (socket.io-client) I've noticed.
Is there anything wrong with just getting the auth information for the handshake (via middleware or on.connection) via cookie?
eg.
io.on('connection', function(socket) {
// assuming base64url token
const cookieStr = socket.handshake.headers.cookie
const matchRes =
cookieStr == null
? false
: cookieStr.match(/my-auth-token=([a-zA-Z0-9_.-]+)/)
if (matchRes) {
// verify your jwt...
if ( tokenIsGood(matchRes[1]) {
// handle authenticated new socket
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
} else {
socket.emit('AUTH_ERR_LOGOUT')
socket.disconnect()
}
}
I'm using this now for a project and it's working fine.
I found a little problem to see the .loggeduser
io.sockets.on('connection', function (socket) {
var endp = socket.manager.handshaken[socket.id].address;
console.log("query... " + socket.manager.handshaken[socket.id].query.loggeduser);
// ↑ here
}
I'm trying to build a real time web page and use postgreSQL as my database. I use node.js and express to build backend stuff. Since this is a real time webpage and needs to update information very frequently, I keep a long connection with postgreSQL, which looks like:
app.get('/:A/:B', function(req,res){
var A = req.params.A;
var B = req.params.B;
var client = new pg.Client(config[A][B]);
client.connect(function(err){
if (err) {
console.log("Error occurred when try to connect the database",err);
}
else {
console.log("Connected to the database");
}
});
Do some queries with current database connection...
}
The problem is, when I change the value of A and B in browser and try to connect to a new database, I didn't disconnect with the old one so the info on my page are still from the old database. I'm new to node and web development. Can anyone let me know how to disconnect with the old database when client try to go to a new url?
I think is not good way to create connection for each request. If size of A-B variants is limited then create of connection pool on start is better.
app.get('/:A/:B', function(req, res, next){ // next to forwarding error
var A = req.params.A;
var B = req.params.B;
var client = new pg.Client(config[A][B]);
client.connect(function(err){
if (err)
return next(err); // go to error-middleware
console.log("Connected to the database");
// Do some queries with current database connection...
// Keep it mind that they're also asynchronous, so better way is use promises or async (https://github.com/caolan/async)
client.end(function (err) {
if (err)
next(err);
});
});
}
// Error middleware
app.use(function(err, req, res, next) {
console.log(req.url, err.message);
})
I'm using node js, express and postgresql as backend.
This is the approach I used to make a rest API:
exports.schema = function (inputs, res) {
var query = knex('schema')
.orderBy('sch_title', 'asc')
.select();
query.exec(function (err, schemas) {
if(err){
var response = {
message: 'Something went wrong when trying to fetch schemas',
thrownErr: err
};
console.error(response);
res.send(500, response);
}
if(schemas.length === 0){
var message = 'No schemas was found';
console.error(message);
res.send(400, message);
return;
}
res.send(200, schemas);
});
};
It works but after a while postgres logs an error and it's no longer working:
sorry, too man clients already
Do I need a close each request somehow? Could not find any about this in the express docs. What can be wrong?
This error only occurs on production server. Not on developing machine.
Update
The app only brakes in one 'module'. The rest of the app works fine. So it's only some queries that gives the error.
Just keep one connection open for your whole app. The docs shows an example how to do this.
This code goes in your app.js...
var Knex = require('knex');
Knex.knex = Knex.initialize({
client: 'pg',
connection: {
// your connection config
}
});
And when you want to query in your controllers/middlewares...
var knex = require('knex').knex;
exports.schema = function (req, res) {
var query = knex('schema')
.orderBy('sch_title', 'asc')
.select();
// more code...
};
If you place Knex.initialize inside an app.use or app.VERB, it gets called repeatedly for each request thus you'll end up connecting to PG multiple times.
For most cases, you don't need to do an open+query+close for every HTTP request.
I'm trying to use app.render() to display a jade file in the browser. In the following code, the html is displayed to the console correctly, but the browser never shows the related file.
app.render('unavailable', {title: 'Unavailable'}, function(err, html){
console.log(html);
});
EDIT:
I have this handler:
app.get('/unavailable', display.unavailable);
Then beneath this code in the same file (app.js) I have this:
sql.open(connStr, function(err, sqlconn){
if(err){
console.error("Could not connect to sql: ", err);
else
conn = sqlconn; //save the sql connection globally for all client's to use
});
So, what I want to happen is when the err happens with the SQL connection, the /unavailable handler is executed and a static html page is displayed that says the service is down. However, because the error occurs on the server, and not the client, I don't have access to a response object at that time. I'm trying to artifically manufacture the client 'redirecting' to /unavailable in their browser to see the message.
Obviously you don't send the html to the browser. Use res.render inside a route without callback, i.e.
res.render('unavailable', {title: 'Unavailable'});
or send the result of rendering like here:
app.render('unavailable', {title: 'Unavailable'}, function(err, html){
console.log(html);
res.send(html);
});
Read more about the difference here:
What's the difference between "app.render" and "res.render" in express.js?
save a global var sqlOK = false, set it in sql.open callback, and redirect to /unavailable if you get a request while sqlOK is not true. you were also missing brackets around the else statement.
var sqlOK = false;
app.get('/unavailable', display.unavailable);
app.get('*', function(req, res, next){
if(!sqlOK){
return res.redirect('/unavailable');
//return res.send(500)
};
next();
});
sql.open(connStr, function(err, sqlconn){
if(err){
console.error("Could not connect to sql: ", err);
} else {
conn = sqlconn; //save the sql connection globally for all client's to use
sqlOK = true
}
});
On my website users need to login with database account. My question is, how to auth socket.io only if database login pass and create my variable in cookie ?
Express with auth function from here socket.io Auth works with sessionID, maybe i can prevent to give sessionID to moment when user log in to database ?
Use the authorization function:
io.set('authorization', function (handshakeData, callback) {
// findDatabyip is an async example function
findDatabyIP(handshakeData.address.address, function (err, data) {
if (err) return callback(err);
if (data.authorized) {
handshakeData.foo = 'bar';
for(var prop in data) handshakeData[prop] = data[prop];
callback(null, true);
} else {
callback(null, false);
}
})
});
Replace findDatabyIP with your database lookup.
Any attributes you set on handshakeData should be available in your "connection" handler via socket.handshake.sessionID.
Call callback when you're done, with an error message (if applicable), and true if the connection is authorized, false if it's not.
Parsing and reading the session's cookie may be difficult. This discussion (especially jugglinmike's post) may help.