request.on(readable) called twice for single request in nodejs - node.js

I am new in nodejs and now days i am learnig http module.
I wrote a js program for node to check if content-type is application/json in request, then it should console at 'readable' event.
What happening is: on a single request 'readable' event called twice and print the value first time and second time it returns null.
Here is Code:
var connect = require('connect');
function jsonParse(req, res, next) {
if (req.headers['content-type'] == 'application/json' && req.method == 'POST') {
var readData = '';
req.on('readable', function() {
console.log('inside readable ' + req.read());
readData += req.read();
});
req.on('end', function() {
try {
req.body = JSON.parse(readData);
} catch (e) {
}
next();
})
} else {
next();
}
}
connect()
.use(jsonParse)
.use(function(req, res) {
if (req.body) {
res.end('JSON parsed !' + req.body);
} else {
res.end('no json detected !');
}
}).listen(3000);
I am calling this like:
output is :
inside readable {
"foo":"asdf"
}
inside readable null
Please guide me how can i handle this. Thanks in advance.

You should only read from the request when data is available, so when req.read() does not return null. You can check this with a while loop.
Replace:
var readData = '';
req.on('readable', function() {
console.log('inside readable ' + req.read());
readData += req.read();
});
With:
var readData = '';
req.on('readable', function(){
var chunk;
while (null !== (chunk = req.read())){
readData += chunk;
}
});

Related

Retrieving key/value from form-data post from client

I write API in order to client upload file. API has content-type multiple/form-data. But I don't know get values from client send to my
router.post('/upload/file', async (req, res) => {
var body = "";
try {
req.on('data', function (chunk) {
body += chunk;
});
req.on('end', function () {
console.log('body: ' + body);
var formData = new FormData(body);
console.log("=====================", formData.entries);
// var {accessTok, type, file} = req.params;
//
// if (!accessTok || !type || !file) {
res.json({
code: -1000,
message: 'Missing parameter(s). Please provide accessToken, type upload, file upload.'
});
res.end();
return null;
})
// }
}catch(err){
res.json({err: err.message});
res.end();
return;
}
I tried use FormData but not done. I get error is not function, formData.getKey('') is them same.

Chaining GET request with a response Node.js

I am trying to perform a GET request to an API and return the data from the API response to the client. I think the client receives a response before the GET request to the API finishes. How can I change the code to ensure that the response from the API is passed on to the client?
if (request.method == 'POST' && request.url == '/locationdata') {
var body = '';
request.on('data', function (data) {
body += data;
});
request.on('end', function () {
var formattedLocation = body.replace(/[\[\]']+/g, '');
var urlAPI = 'https://api.darksky.net/forecast/166731d8eab28d33a26c5a51023eff4c/' + formattedLocation;
response.writeHead(200, { 'Content-Type': 'application/json' });
var apiData = '';
var apirequest = function () {
https.get(urlAPI, function (response) {
response.on('data', function (data) {
apiData += data;
});
response.on('end', function () {
console.log(apiData);
return apiData;
});
});
}
response.end(apirequest);
});
return;
}
You are ending the response to the client before you get all the data from the api. Moving the response.end() call up to the end of the api response should fix it:
if (request.method == 'POST' && request.url == '/locationdata') {
var body = '';
request.on('data', function (data) {
body += data;
});
request.on('end', function () {
var formattedLocation = body.replace(/[\[\]']+/g, '');
var urlAPI = 'https://api.darksky.net/forecast/166731d8eab28d33a26c5a51023eff4c/' + formattedLocation;
response.writeHead(200, { 'Content-Type': 'application/json' });
var apiData = '';
https.get(urlAPI, function (apiResponse) {
apiResponse.on('data', function (data) {
apiData += data;
});
apiResponse.on('end', function () {
console.log(apiData);
// send response to browser after we get all the data from the api
response.end(apiData);
});
});
// remove this because we moved it up
//response.end(apirequest);
});
return;
}

Dealing with asynchronous http calls node js

I have a RESTish api on a node js server. When it receives a GET request, it is to call a function that will then be calling another server get function. The code looks like this:
var server = http.createServer(function(request, response) {
console.log("YEAHHH! ", request.method);
var string='';
// Inside a request handler method
if (request.method == "OPTIONS") {
console.log("options");
// Add headers to response and send
//response.writeHead(statusCode, responseHeaders);
response.writeHead(success,responseHeaders);
response.end();
}
if(request.method == "GET") {
string = soso();
}
console.log("*******", string);
response.writeHead(success,responseHeaders);
response.end(string);
});
soso() is the call to the other server. The issue is I want to send the response of the soso() function before its finished so all I'm getting is an empty string.
How do I get around this?
I'm sure this is a duplicate but can't quite find what I'm looking for. So, any help is appreciated.
EDIT
Code for the soso function:
var soso = function () {
console.log("this is being called");
var options = {...}
var req = https.get( options, function(res) {
var str = '';
res.on('data', function ( chunk ) {
str += chunk;
})
res.on('end', function () {
console.log ( "str is: ", str );
string = str;
})
req.end();
console.log(res.statusCode);
console.log(responseHeaders);
});
}
You can try something like this , I am not sure. I have not tested this. Pass the response object in soso function, then use response.end(string); after get request gets end.
var server = http.createServer(function(request, response) {
console.log("YEAHHH! ", request.method);
var string='';
// Inside a request handler method
if (request.method == "OPTIONS") {
console.log("options");
// Add headers to response and send
//response.writeHead(statusCode, responseHeaders);
response.writeHead(success,responseHeaders);
response.end();
}
if(request.method == "GET") {
string = soso(response); //send response object as argument
} else {
console.log("*******", string);
response.writeHead(success,responseHeaders);
response.end(string);
}
});
soso function
var soso = fuction(response){ // we pass the response object in soso function
console.log("this is being called");
var options = {...}
var req = https.get( options, function(res) {
var str = '';
res.on('data', function ( chunk ) {
str += chunk;
})
res.on('end', function () {
console.log ( "str is: ", str );
string = str;
})
req.end();
response.writeHead(success,responseHeaders);
response.end(string);
console.log(res.statusCode);
console.log(responseHeaders);
});
}

Limiting outside API requests

I'm trying to limit my use of an external API in my node.js code.
I've set up node rate limiter, but it doesn't seem to be working. I still hit 429's. What else should I be doing that I'm not?
var RateLimiter = require('limiter').RateLimiter; // Rate limits
var limiter = new RateLimiter(1, 2000); // one call every two seconds
self.riotAPI = function(options, cb){
limiter.removeTokens(1, function() {
https.request(options, function(response) {
// Error handling
response.on('error', function (e) {
console.log(e);
});
var str = '';
// Another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
});
// Parse and return the object
response.on('end', function () {
if(response.statusCode >= 400) {
var err = "HTTP response "+response.statusCode;
console.log(err);
cb(new Error("err"), null);
}
else {
cb(null, JSON.parse(str));
}
});
}).end();
});
}
I switched to Bottleneck and got everything functioning as desired.
self.riotAPI = function(options, cb){
limiter.submit( function(lcb) {
https.request(options, function(response) {
// Error handling
response.on('error', function (e) {
console.log(e);
});
var str = '';
// Another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
});
// Parse and return the object
response.on('end', function () {
if(response.statusCode >= 400) {
var err = "HTTP response "+response.statusCode;
console.log(err);
// If it's a 429, retry
if(response.statusCode == 429) {
console.log("retrying...");
self.riotAPI(options, cb);
}
// If not, fail
else {
cb(new Error("err"), null);
lcb();
}
}
else {
cb(null, JSON.parse(str));
lcb();
}
});
}).end();
}, null);
}

NodeJS data throughput

I've set up a NodeJS server which can be accessed by a client. Every once in a while it's necessary to let the server connect to a second server and feed the information retrieved back to the client.
Connecting to the second server is the easy part, but to be honest I have no idea how to send it back to the client. res.write seems to be forbidden during the connection with the second server.
The connection from the client is handled by handleGetRequest. The connection with the second server starts at http.get.
var http = require('http');
var url = require('url');
var server = http.createServer(function(req, res) {
var url_parsed = url.parse(req.url, true);
if (req.method ==='GET') {
handleGetRequest(res, url_parsed);
} else {
res.end('Method not supported');
}
});
handleGetRequest = function(res, url_parsed) {
if (url_parsed.path == '/secondary') {
var OPTIONS = {
hostname: "localhost",
port: "8900",
path: "/from_primary"
}
http.get(OPTIONS, function(secget) {
resget.on('data', function(chunk) {
// either store 'chunk' for later use or send directly
});
}).on('error', function(e) {
console.log("Error " + e.message);
});
} else {
res.writeHead(404);
}
res.end('Closed');
};
server.listen(8000);
How do I send the chunk from http.request to the client?
I thinks passing the callback to the handleGetRequest will fix this issue:
if (req.method === 'GET') {
handleGetRequest(url_parsed, function (err, response) {
if (err) {
return res.sendStatus(500);
}
res.json(response);
});
} else {
res.end('Method not supported');
}
handleGetRequest = function (url_parsed, callback) {
// OPTIONS ...
http.get(OPTIONS, function(resget) {
var data = '';
resget.on('data', function(chunk) {
data += chunk;
});
resget.on('end', function() {
callback(null, data);
});
}).on('error', function(e) {
callback(e);
});
}
Thanks to #TalgatMedetbekov for the suggestions. I managed to implement it like this:
var http = require('http');
var url = require('url');
var server = http.createServer(function(req, res) {
var url_parsed = url.parse(req.url, true);
if (req.method ==='GET') {
handleGetRequest(res, url_parsed);
} else {
res.end('Method not supported');
}
});
handleGetSecondaryRequest = function(callback, res) {
var OPTIONS = {
hostname: "localhost",
port: "8900",
path: "/from_primary"
}
var data = null;
http.get(OPTIONS, function(func, data) {
func.on('data', function(chunk) {
data += chunk;
});
func.on('end', function() {
callback(res, data);
});
}).on('error', function(e) {
callback(res, e);
})
};
var secReqCallback = function(res, recData)
{
res.write(recData);
res.end("END");
};
handleGetRequest = function(res, url_parsed) {
if (url_parsed.path == '/secondary') {
handleGetSecondaryRequest(secReqCallback, res);
} else {
res.writeHead(404);
}
};
server.listen(8000);
It works, kind of. There's an 'undefined' in front of the string which I can't find the cause for, but the basic functionality works perfect.
The callback construction is necessary to synchronize the asynchronous nature of NodeJS.

Resources