nodejs http request returns `undefined` - node.js

While trying to debug the following get request, I notice that it returns undefined and then runs the code for the response.
configs is a json object with all the parameters defined. I am also, for some reason, getting a response form the php server saying that grant-type is invalid or can't be found, although when debugging it is passing the correct parameter from the configs file.
How can I correct my code?
var http = require("http");
var querystring = require("querystring");
var _ = require("underscore");
apiCaller = {};
apiCaller.token = null;
var server=http.createServer(function(req,res){});
server.listen(8080);
apiCaller._get = function (context, config, fn) {
// request to obtain our oauth token
var options = {
method: "GET",
hostname: config.host,
client_id: config.clientId,
client_secret: config.clientSecret,
grant_type: config.grant_type,
path: "/my/path/to/token",
headers : {
'Content-Type': "application/json",
'Accept': "application/json"
}
};
var callback = function(response) {
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + JSON.stringify(response.headers));
var str = '';
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
});
// error response
response.on("error", function (error) {
if ( !context ) {
console.error("Something went wrong with the api response.");
return;
}
context.done(new Error("Something went wrong with the api response."));
});
//the whole response has been recieved, so we just print it out here
response.on('end', function () {
apiCaller.token = JSON.parse(str).access_token;
// we want to stop the request if token is not correct
if ( !apiCaller.token || apiCaller.token === undefined || apiCaller.token === null ) {
if ( !context ) {
console.error("Something went wrong with the token. Wrong token! Token: %s", apiCaller.token);
return;
}
console.error("Token: %s", apiCaller.token);
context.done(new Error("Something went wrong with the token. Wrong token!"));
}
});
};
var request = http.request(options, callback);
request.on('error', function(e) {
console.log('problem with request:');
});
request.end();
};

It is an asynchronous function. Asynchronous functions (which are kind of the bread-and-butter of Node.js) typically return nothing. Instead, what you might think of as the return value is passed to the callback function. That's what's happening here.

As Trott says, It's asynchronous, it's possible that request.end() is executing before callback function has finished....

Related

How to make https call from Azure function?

I'm trying to create an Azure function using nodeJS, but when I make a call to an https API I get an error message.
Is it possible to make a HTTPS call from azure function?
Here is my code
const https = require('https');
const querystring = require('querystring');
module.exports = async function (context, req) {
if (req.query.accessCode || (req.body && req.body.accessCode)) {
var options = {
host: 'api.mysite.com',
port: 443,
path: '/oauth/access_token',
method: 'POST'
};
var postData = querystring.stringify({
client_id : '1234',
client_secret: 'xyz',
code: req.query.accessCode
});
var req = https.request(options, function(res) {
context.log('STATUS: ' + res.statusCode);
context.log('HEADERS: ' + JSON.stringify(res.headers));
res.on('data', function (chunk) {
context.log('BODY: ' + chunk);
});
});
req.on('error', function(e) {
context.log('problem with request: ' + e.message);
});
req.write(postData);
req.end();
context.res = {
status: 200,
body: "Hello " + (req.query.accessCode)
};
} else {
context.res = {
status: 400,
body: "Please pass a name on the query string or in the request body"
};
}
context.done();
};
I get an error but I do not see any error on the console, also if I comment all the https call it works fine and I can see the Hello message on the screen.
Two points to fix
Delete context.done();. See Azure document.
If your function uses the JavaScript async function declaration (available using Node 8+ in Functions version 2.x), you do not need to use context.done(). The context.done callback is implicitly called.
Rename your https.request like var myReq = https.request(options, function(res).
There's a name conflict causing error as function has a built-in req object declared.
It's possible, here is an example of how to make a request to an Azure AD v2 token endpoint (I'd assume you are trying to do something similar):
var http = require('https');
module.exports = function (context, req) {
var body = "";
body += 'grant_type=' + req.query['grant_type'];
body += '&client_id=' + req.query['client_id'];
body += '&client_secret=' + req.query['client_secret'];
body += '&code=' + req.query['code'];
const options = {
hostname: 'login.microsoftonline.com',
port: 443,
path: '/ZZZ920d8-bc69-4c8b-8e91-11f3a181c2bb/oauth2/v2.0/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': body.length
}
}
var response = '';
const request = http.request(options, (res) => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', (d) => {
response += d;
})
res.on('end', (d) => {
context.res = {
body: response
}
context.done();
})
})
request.on('error', (error) => {
context.log.error(error)
context.done();
})
request.write(body);
request.end();
};
The difference is - the function is not async module.exports = function
I believe your issue is:
You should use the Node.js utility function util.promisify to turn error-first callback-style functions into awaitable functions.
link

http GET from node.js

I'm fumbling my way through node.js with massive help from people on here and I'm struggling getting the body of a GET request into a variable.
Here's the code so far:
var speechOutput;
var myCallback = function(data) {
console.log('got data: '+data);
speechOutput = data;
};
var usingItNow = function(callback) {
var http = require('http');
var url = 'http://services.groupkt.com/country/get/iso2code/IN';
var req = http.get(url, (res) => {
var body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
var result = JSON.parse(body);
callback(result);
});
}).on('error', function(e){
console.log("Got an error: ", e);
});
};
usingItNow(myCallback);
I'm using examples from other posts to try and get the body of the GET request into the speechOutput variable but it is coming out as undefined.
Ultimately I want the RestResponse.result.name in speechOutput, but I thought I would take this one step at a time. Can anyone offer any pointers?
Further to this, I have tried the following, which still came back undefined - maybe there is a bigger issue with the code? It doesn't even seem to be getting to the parse.
res.on("end", () => {
// var result = JSON.parse(body);
callback('result');
});
putting the line callback('result'); before the line var req = http.get(url, (res) => { returns 'result' but anything else is either undefined or causes an error.
Quoting Roy T. Fielding:
Server semantics for GET, however, are restricted such that a body,
if any, has no semantic meaning to the request. The requirements
on parsing are separate from the requirements on method semantics.
Don't use get request to send body parameters. Use post requests. If you want to send data within a get request, add them to the query string.
Read this for more info about bodies in get requests:
HTTP GET with request body
Update:
Try to log errors in the response, add this before you set up the listeners:
var body = "";
const { statusCode } = res;
const contentType = res.headers['content-type'];
let error;
if (statusCode !== 200) {
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// consume response data to free up memory
res.resume();
return;
}
res.on("data", (chunk) => {
body += chunk;
});

Node.js "callback not defined" when trying to to a http request

totally new to Node.js and this callback thing is driving me nuts.
I am writing a Skill for an Amazon Echo. As part of this I am trying to send an SMS using BulkSMS.com via a HTTP Request. The http.request has a callback which parses the response. (To take the BulkSMS API out of the equation in the example below I am just trying to get it working using a http request to Random.org (i.e. www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new)
However I am getting an error saying that the callback is not defined.
{
"errorMessage": "callback is not defined",
"errorType": "ReferenceError",
"stackTrace": [
"Emergency.eventHandlers.onLaunch (/var/task/index.js:54:11)",
"AlexaSkill.requestHandlers.LaunchRequest (/var/task/AlexaSkill.js:16:37)",
"AlexaSkill.execute (/var/task/AlexaSkill.js:97:24)",
"exports.handler (/var/task/index.js:100:15)"
]
}
I am sure I am doing something totally stupid and for that I apologize, but I just can't see it.
Here is the onLaunch event handler in my index.js. Any help greatly appreciated.
Emergency.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
console.log("Emergency onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
console.log("Attempting to send SMS");
callback = function(response) {
var str = '';
console.log("In callback");
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
console.log("Getting Data");
});
//the whole response has been recieved, so we just print it out here
response.on('end', function () {
console.log("End of response");
console.log(str);
});
}
http.request(options, callback).end();
console.log("Finished sending SMS");
var speechOutput = "<speak>SMS sent</speak>";
response.tell(speechOutput);
};
Full index.js is below.
'use strict';
/**
* App ID for the skill
*/
var APP_ID = "amzn1.ask.skill.eb8cf94a-848f-45ae-9792-xxxxxxxxxx";
/**
* The AlexaSkill prototype and helper functions
*/
var AlexaSkill = require('AlexaSkill');
var http = require('http');
var request = require("request");
//var Alexa = require('alexa-sdk');
var SKILL_NAME = 'Emergency';
var Emergency = function () {
AlexaSkill.call(this, APP_ID);
console.log("APP_ID set");
};
var alexaResponse = "";
//The url we want is: 'www.random.org/integers/? num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
var options = {
host: 'www.random.org',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
// Extend AlexaSkill
Emergency.prototype = Object.create(AlexaSkill.prototype);
Emergency.prototype.constructor = Emergency;
Emergency.prototype.eventHandlers.onSessionStarted = function (sessionStartedRequest, session) {
console.log("Emergency onSessionStarted requestId: " + sessionStartedRequest.requestId
+ ", sessionId: " + session.sessionId);
// any initialization logic goes here
};
Emergency.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
console.log("Emergency onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
console.log("Attempting to send SMS");
callback = function(response) {
var str = '';
console.log("In callback");
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
console.log("Getting Data");
});
//the whole response has been recieved, so we just print it out here
response.on('end', function () {
console.log("End of response");
console.log(str);
});
}
http.request(options, callback).end();
console.log("Finished sending SMS");
var speechOutput = "<speak>SMS sent</speak>";
response.tell(speechOutput);
};
Emergency.prototype.eventHandlers.onSessionEnded = function (sessionEndedRequest, session) {
console.log("Emergency onSessionEnded requestId: " + sessionEndedRequest.requestId
+ ", sessionId: " + session.sessionId);
// any cleanup logic goes here
};
Emergency.prototype.intentHandlers = {
// register custom intent handlers
EmergencyIntent: function (intent, session, response) {
// Get a random "never" phrase from the list
}
};
// Create the handler that responds to the Alexa Request.
exports.handler = function (event, context) {
// Create an instance of the Emergency skill.
var emergency = new Emergency();
emergency.execute(event, context);
};
However I am getting an error saying that the callback is not defined.
That's because of 'use strict'; mode.
First, strict mode makes it impossible to accidentally create global variables. In normal JavaScript mistyping a variable in an assignment creates a new property on the global object and continues to "work" (although future failure is possible: likely, in modern JavaScript). Assignments which would accidentally create global variables instead throw in strict mode:
More on this in MDN
accidentalGlobalVariable = 'hello world';
console.log(accidentalGlobalVariable); // logs hello world
In strict mode,
'use strict';
accidentalGlobalVariable = 'hello world';
console.log(accidentalGlobalVariable); // errors out
Error
accidentalGlobalVariable = 'hello world';
^
ReferenceError: accidentalGlobalVariable is not defined
In your code sample, If I run without strict mode it works,
var http = require('http');
var options = {
host: 'www.random.org',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
callback = function (response) {
console.log('response statusCode', response.statusCode);
};
http.request(options, callback).end();
but if I run with strict mode,
'use strict';
var http = require('http');
var options = {
host: 'www.random.org',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
callback = function (response) {
console.log('response statusCode', response.statusCode);
};
http.request(options, callback).end();
I too get error because strict mode errors out callback
callback = function (response) {
^
ReferenceError: callback is not defined
you can remedy it by
Using var or let in assignment.
var callback = function (response) {
console.log('response statusCode', response.statusCode);
};
Eliminating the need of callback all together and use an anonymous function instead.
http.request(options, function (response) {
console.log('response statusCode', response.statusCode);
}).end();

I can't save http-headers to the file by piping these to stream in node.js

var fsHeaders = fs.createWriteStream('headers.html', 'utf-8');
var getHeaders = request('http://google.com', {method: 'HEAD'}, function (error, response, body)
{
if (error || response.statusCode !== 200)
{
return res.redirect('/');
}
if (!error && response.statusCode == 200)
{
var tester = response.body;
console.log(tester + 'response.body is EMPTY... so the headers are stored not in the "body"... \n');
var targetHeaders = response.headers;
body = body + JSON.stringify(targetHeaders);
console.log(body + 'OK! \n');
}
}).pipe(fsHeaders);
but my headers.html is empty... how to properly save response.headers to file? Can I later modify headers and sent them to the user without problem or it's restricted in some way? I want to get data from server, modify it via node, and send it to the user.
It doesn't matter if you are using request or http module. When you request headers only with {method: 'HEAD'} method body is empty but headers are resident in response stream. I guess you problem is with requiring headers and display them as [objest Object]. You need to change object to string in that matter - to simply display it with console.log or pipe it to the file.
var targetHeaders = objectToString(resp.headers, targetHeaders);
function objectToString(object, outputString)
{
var outputString = '';
for (var i in object)
{
if (object.hasOwnProperty(i))
{
outputString += i + ': ' + object[i] + ' \n';
}
}
return outputString;
}
now console.log(targetheaders); // OK! no [object Object] anymore :)
You can latter pass those headers without any problem.
First, to verify that you are using http.request
Your problem also can be because request by default returns buffer and your stream is UTF-8 format.
Also the better way "catch" your headers might be by events that are emitted by http request:
var postData = querystring.stringify({
'msg': 'Hello World!'
});
var options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
var req = http.request(options, (res) => {
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.log(`problem with request: ${e.message}`);
});
// write data to request body
req.write(postData);
req.end();

SailsJS - Nodejs Https-Request. Can't set headers after they are sent

I'm new to Sails.js and I was trying to make a filter to authorize using a Bearer token which come from a higher server, a gatekeeper which is responsable to do the OAuth2 authentication from GitHub API. The services streams works well. I'm already aware of Passport.js but I'm trying to implement this on my own. I came with a policy which looks like:
module.exports = function (req, res, next) {
var httpsExec = require('https');
if (req.headers.authorization) {
var parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
var tokenType = parts[0]
, credentials = parts[1];
if (/^Bearer$/i.test(tokenType) || /^bearer$/i.test(tokenType)) {
httpsExec.request({
host: 'api.github.com',
post: 443,
path: '/user',
method: 'GET',
headers: {'Authorization': 'token ' + credentials, 'User-Agent': 'curly'}
}, function (response) {
var responseData = '';
response.setEncoding('utf8');
response.on('data', function (chunk) {
responseData += chunk;
});
response.once('error', function (err) {
next(err);
});
response.on('end', function () {
try {
req.options.user = JSON.parse(responseData);
next();
} catch (e) {
res.send(401, {error: e});
}
});
}).end();
} else {
console.err("The token is not a Bearer");
res.send(401)
}
}
} else {
res.send(401, {error: "Full authentication is necessary to access this resource"})
}
};
The policy is called once I hit the controller route but it throws a _http_outgoing.js:335
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
And the process is terminate.
The problem I think is the next() and the returns I tried everywhere I think, to put the next() call, but still gives me this error, if I remove then I lock the request on the policy.
EDIT
I did a simple sample of policy where I just set some property on req.options.values and happened the same problem, so maybe could be an issue with req.options.requestData = JSON.parse(responseData); ? How else could I set a property to send to controller ?
response.once('error', function (err) {
next(err);
});
response.on('end', function () {
try {
req.options.user = JSON.parse(responseData);
next();
} catch (e) {
res.send(401, {error: e});
}
});
both are getting executed.to check console.log("something") in error to see if there is error.
This happens when you're trying to modify the request and response together or modify one of them twice.
In your code, I think the callback is being called twice and you are also modifying the response at the same time. Check the lines where you're calling callback "next()". You'll find your issue.

Resources