0

I'm having a strange Can't set headers after they are sent crash on my NodeJs/Express app.

The crashing request : POST /auth/form

How the request is server-side handled

app.js

[...]
var auth_route = require('./routes/auth');
app.use('/auth', auth_route );
[...]

auth.js

var AuthController = require('../controller/auth_controller');

[...]

router.post("/form", function(req, res) {
    [...]
    auth_ctrl.form_login(username, password);
});

auth_controller.js

AuthController.prototype.form_login = function(username, password) {
    _this.user_model.get_by_username(username, function(user) {
        if (user == null)
            return (Response.send_204(_this.res, "User not found"))
        password.isTheSame(password, user.password, function(err, res) {
            if (err)
                return (Response.send_error_response(_this.res, 500, "Internal server error occured, sorry about that.."));
            if (!res)
                return (Response.send_error_response(_this.res, 401, "Wrong password"));
            // Crash seems to happen on the above 401 response which is the 67th lines of my auth_controller file (cf. callstack bellow)
            _this.start_user_session(user, function(err) {
                if (err) 
                    return (Response.send_error_response(_this.res, 500, "Internal server error"));
                return (Response.send_200(_this.res, "You are logged!"));
            })
        });
    })
}

Response.send_error_response source code if needed

function send_error_response(res, code, message, missing_fields) {
    [..]

    res.header('Content-Type', 'application/json')
    res.status(code).send(JSON.stringify({
        [..]
    }));
}

The callstack trace

POST /auth/form 401 2.408 ms - -
_http_outgoing.js:356
    throw new Error('Can\'t set headers after they are sent.');
    ^

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:356:11)
    at ServerResponse.header (C:\Users\ME\dev\app\node_modules\express\lib\response.js:719:10)
    at Object.send_error_response (C:\Users\ME\dev\app\core\lib\Response.Library.js:30:6)
    at C:\Users\ME\dev\app\controller\auth_controller.js:67:22

How I make this crash happen

I keep pushing down my enter button which send a looot of requests from the same origin.

It seems that's happening inside password callback function...

Any suggestions ? Thanks in advance !

Dash
  • 742
  • 7
  • 19
  • 1
    You're missing a closing quote on this line after `password` `return (Response.send_error_response(_this.res, 401, "Wrong password));` – GillesC May 12 '17 at 09:41
  • See here http://stackoverflow.com/questions/7042340/error-cant-set-headers-after-they-are-sent-to-the-client – Patrick R May 12 '17 at 09:57
  • @GillesC Thank for noticing ! I edited. – Dash May 12 '17 at 10:25

1 Answers1

2

My guess is that your implementation of AuthController does something like this:

var _this;
function AuthController(req, res) {
  this.req = req;
  this.res = res;
  _this = this;
}

This "promotes" _this to a (module-wide) global, which gets overwritten for every request that posts to /auth/form. If two of those requests are sent in quick succession, you could end up with the situation that a response is send using the same _this more than once, which would result in the error that you got:

  • request 1 comes in, _this points to its controller instance
  • request 2 comes in, _this gets overwritten to point to its controller instance
  • request 1 is done and sends back a response, using the _this that belongs to request 2
  • request 2 is done and sends back a response, using the same _this, resulting in an error

So, don't use globals, use this instead:

AuthController.prototype.form_login = function(username, password) {
    this.user_model.get_by_username(username, function(user) {
      ...
    });
};

You can always create a function-scoped variable to hold a reference to it, if preferable:

AuthController.prototype.form_login = function(username, password) {
    var _this = this;
    _this.user_model.get_by_username(username, function(user) {
      ...
    });
};
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • Thanks for your answer ! First thing, you are right on the implementation guess, congrat's ;-) Second thing, the issue is, in fact, coming from the use of global `_this`. Last thing, I may use the function-scoped variable because otherwise, `return (Response.send_error_response(_this.res, 401, "Wrong password));` is resulting in a _TypeError: Cannot read property 'header' of undefined_ in the **Response.send_error_response** function.. Or maybe there is an alternative? – Dash May 12 '17 at 10:21
  • @Maincore an alternative would be to use [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), which preserve `this` (see [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_binding_of_this)), but it's something you have to feel comfortable with. If not, using a function scoped variable is the easiest solution. – robertklep May 12 '17 at 10:28
  • Funny thing, I was checking your github and wondering what were these weird functions that you were using, here is my answer ahah! Well, it seems that the basic usage of those functions is pretty similar to the common function usage. Except that the syntax and the scope of `this` is different (maybe there are more diffences but in my case it's not relevent) – Dash May 12 '17 at 10:37