0

The error in question

Error: Can't set headers after they are sent.

server.js

// set up ======================================================================
var express = require('express')
    , app = express()
    , cookieParser = require('cookie-parser')
    , bodyParser = require('body-parser')
    , expressSession = require('express-session')
    , server = require('http').createServer(app)
    , passport = require('passport')
    , local = require('passport-local').Strategy
    , md5 = require('md5')
    , util = require('util')
    , flash = require('connect-flash')
    , port = 80
    , url = require('url')
    , db = require('./db');

// optimization ================================================================
var compress = require('compression');
app.use(compress());

// configuration ===============================================================
app.use(express.static('public'));
app.use(cookieParser());
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

app.use(expressSession({
    secret: 'key',
    resave: false,
    saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());

// passport ====================================================================
passport.use(new local(
    function(username, password, done) {
        // asynchronous verification, for effect...
        process.nextTick(function () {
            db.findUserByName(username, function (err, user) {
                if (err) { done(err); }
                if (!user) { done(null, false, {message: 'Unknown user: ' + username})}
                if (md5(password) == user.password) {
                    done(null, user);
                } else {
                    done(null, false, {message: 'Invalid username or password'});
                }
            });
        });
    }
));
passport.serializeUser(function(user, done) {
    done(null, user.uuid);
});

passport.deserializeUser(function (id, done) {
    db.findUserByUUID(id, done);
});

// routes ======================================================================
require('./routes')(app, passport);

// launch ======================================================================
server.listen(port, function(){
    console.log('server started');
});

routes.js

var flash = require('connect-flash');

module.exports = function(app, passport) {

    app.get('/', function(req, res) {
        res.render('index', { num: 0, logged: false });
    });

    app.get('/login', function (req, res) {
        if (typeof req.user !== 'undefined') {
            // User is logged in.
            res.redirect('/');
        } else {
            req.user = false;
            var message = req.flash('error');
            if (message.length < 1) {
                message = false;
            }
            res.render('login', { logged: false, message: message });
        }
    });

    app.post('/login',
        passport.authenticate('local', {
            failureRedirect: '/login',
            failureFlash: true
        }),
        function(req, res) {
            res.redirect('/');
        }
    );

    app.get('/logout', function(req, res){
        req.logout();
        res.redirect('/');
    });


};

db.js

var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var ObjectId = require('mongodb').ObjectID;
var url = 'mongodb://localhost:27017/db';

exports.findUserByName = function (username, callback) {
    MongoClient.connect(url, function(err, db) {
        var cursor = db.collection('players').find( { "name": username } );
        cursor.each(function(err, doc) {
            if (doc != null) {
                console.log('YES');
                callback(false, doc)
            } else {
                console.log('NO');
                callback(false, null);
            }
            db.close();
        });

    });
};

var findUserByUUID = function(uuid, db, callback) {
    var cursor = db.collection('players').find( { "uuid": uuid } );
    cursor.each(function(err, doc) {
        assert.equal(err, null);
        if (doc != null) {
            callback(false, doc)
        } else {
            callback(false, null);
        }
    });
};
exports.findUserByUUID = function (uuid, callback) {
    MongoClient.connect(url, function(err, db) {
        assert.equal(null, err);
        findUserByUUID(uuid, db, function(err, data) {
            callback(err, data);
            db.close();
        });
    });
};

I understand that i'm calling done() twice, but when? I'm unable to find it. I suppose that the problem is on the routes, but I don't know where.

bernatixer
  • 83
  • 1
  • 2
  • 6
  • Look at [this](http://stackoverflow.com/questions/7042340/node-js-error-cant-set-headers-after-they-are-sent) and see if that can solve your problem. – baranskistad Sep 11 '16 at 02:20

1 Answers1

0

In your passport.use() handler, each time you call done(), you need to then return so that the code after it is not also executed.

Right now you have a series of if statements that can each call done(), but they aren't if/else statements so more than one condition you are testing for can be met and thus done() can be called more than once.

For example, change this:

if (!user) { done(null, false, {message: 'Unknown user: ' + username})}

to this:

if (!user) { 
    done(null, false, {message: 'Unknown user: ' + username});
    return;
}

And, change this:

if (err) { done(err); }

to this:

if (err) { 
    done(err); 
    return;
}

Or, you could make everything into if/else if/else if/else such that only one branch could ever execute. The problem occurs when more than one of your if statements has its condition met and then is executed.


Now, your findUserByName() calls its callback in cursor.each() so it can call it multiple times which then causes you to call done() multiple times.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I've changed everything to an if/else if/else if/else statement, but I'm still getting the same error. I've posted the DB code. – bernatixer Sep 11 '16 at 07:52
  • @bernatixer - Your `findUserByName()` calls its callback in `cursor.each()` so it can call it multiple times which then causes you to call `done()` multiple times. – jfriend00 Sep 11 '16 at 14:45