Friday 9 December 2016

node.js - Update headers in a nodejs http.request



There is a way in nodejs to modify response headers when calling a second nested http.request that return some data from another server ?
I always get this error, from the below functions...




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


Main function from where I call to check if the token is valid:
req.on('end', function (){
console.log('GET to /api, Do I have privileges with %s?', access_token);
validadToken(req, res, next);
});



This is the funciton to validate the token against the OAuth2 server, if the respond is 401, I tried to obtain an new refresh_token:




    function validadToken (req, res, next ) {
var options = { port: 8080,
path: '/api',
host: 'localhost',
method: 'GET' ,
headers: {'Authorization': 'Bearer '+ access_token,
'Content-Type':'application/x-www-form-urlencoded'} };
req2 = http.request(options,
function (response){

response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log(".-response=> ", chunk);
console.log("statusCode: " + response.statusCode);

if(response.statusCode == 401){
console.log();
console.log("===>>>> call to renew / wait for me");
renewToken(response, res, next);


}else if(response.statusCode === 200){
console.log("Valid Token... we keep it going!");
}
next();
});
});
req2.write(formData);

req2.end();
}



From here I try to get the refresh token, but when I try to update the new value in the headers I get the above error. The value for refresh_token is recovery from a response from the previously function



    function renewToken (req, res){
var userUrl = 'grant_type=refresh_token&refresh_token='+refresh_token+'&client_id=XXX&client_secret=XXX';
var optRenew = { port: 8080,
path: '/token',
host: 'localhost',
method: 'POST' ,

headers: {'Content-Type':'application/x-www-form-urlencoded'} };

renew = http.request(optRenew,
function (responseRenew){
console.log("responseRenew");
responseRenew.setEncoding('utf8');

responseRenew.on('data', function (chunk){
console.log(".-responseRenew=> ", chunk);
console.log(".-responseRenew=> ", responseRenew.statusCode);

accessTokenValue = getToken("access_token", chunk, ',', ':', '"');
refreshTokenValue = getToken("refresh_token", chunk, ',', ':', '"');
console.log("Headers Sent (renew) ? "+ res.headersSent);
console.log("¡¡¡New values!!!\n" + accessTokenValue +' '+ refreshTokenValue);

res.setHeader("Set-Cookie", ["ninja="+accessTokenValue, "samurai="+refreshTokenValue]);
console.log("envio ....");
})
});
renew.write(userUrl);

renew.end();
}

Answer



I think I found it



if(response.statusCode == 401) {
console.log();
console.log("===>>>> call to renew / wait for me");
renewToken(response, res, next);


} else if(response.statusCode === 200) {
console.log("Valid Token... we keep it going!");
}
next();


When the token doesn't need to be renewed you call next and all is ok, but when renewToken is called you have a problem: http.request is an asynchronous call, renewToken returns before the code that process the renew response is executed, so the call to next is done before the renewed token is known. When you call next the next middleware is called, here probably the next middleware will send back the response before the renewed token will be available and this explains your error: after the next middleware completes the response you try to set one of the response headers



The solution actually is really easy (I'll skip some code)




function validadToken(req, res, next) {
...
req2 = http.request(options, function (response) {
response.setEncoding('utf8');
response.on('data', function(chunk) {
...
if (response.statusCode == 401) {
...
renewToken(req, res, next);


} else if (response.statusCode === 200) {
console.log("Valid Token... we keep it going!");
next();
} else {
// TODO: you should deal with the other cases
}
});
});
req2.write(formData);

req2.end();
}

function renewToken(req, res, next) {
...

renew = http.request(optRenew, function (responseRenew){
...
responseRenew.on('data', function(chunk){
...

res.setHeader("Set-Cookie", ["ninja="+accessTokenValue, "samurai="+refreshTokenValue]);
console.log("envio ....");

// NOW IT'S THE TIME TO CALL THE NEXT MIDDLEWARE
next()
})
});
renew.write(userUrl);
renew.end();
}



TL;DR; you should call next only after you got the token


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...