Using node-rio from Meteor Server Side Route - node.js

I'm trying to access an Rserve server using Meteor through a server side route to call R code. The Node.js module that allows access to Rserve is node-rio I have used the Async.wrap function in the meteorhacks:npm Meteor package to wrap the "evaluate" method. When I try to access the route path "/rio" in a browser, I get "1" written to the console log, which is correct for the evaluation of the command "1" in R, but Chrome hangs with the message "Waiting for localhost". It doesn't proceed to the next line and "Result: 1" is never shown. The message "net::ERR_EMPTY_RESPONSE eventually shows up in Chrome.
Router.route('/rio', function() {
var rio = Meteor.npmRequire('rio');
var evalSync = Async.wrap(rio, 'evaluate');
var result = evalSync('1');
console.log("Result: " + result);
// JSON
this.response.writeHead(200, {'Content-Type': 'application/json'});
this.response.end(result);
}, {
where: 'server'
});
"evaluate" has a callback function parameter, but it is wrapped in an options parameter and accessed:
rio.evaluate(R_COMMAND, {callback: CALLBACK_FUNCTION})

Two errors:
as you already noted rio expects the callback as a value to the callback field in the parameters, not straight in the arguments themselves.
your result ("1") wasn't actually valid JSON.
This works:
Router.route('/rio', function() {
var rio = Meteor.npmRequire('rio');
var evalSync = Async.wrap(function(exp, callback) {
rio.evaluate(exp, {callback: callback});
});
var result = evalSync('1');
console.log("Result: " + result);
// JSON
this.response.writeHead(200, {'Content-Type': 'application/json'});
this.response.end(JSON.stringify({result: result}));
}, {
where: 'server'
});

This answer will produce the expected result for 'packageVersion("base")', it uses the Node module rserve-client instead of rio to connect to Rserve. The route also handles the parameter 'pkg'. This is the StackOverflow question that pointed me in the right direction:
How to call async method from Meteor own callbacks?
Router.route('rserve', {
path: '/rserve/:pkg',
where: 'server',
action: function() {
var r = Meteor.npmRequire("rserve-client");
var Future = Meteor.npmRequire("fibers/future");
var fut = new Future();
var cmd = 'packageVersion("' + this.params.pkg + '")';
var callR = function (input) {
r.connect('127.0.0.1', 6311, function (err, client) {
client.evaluate(input, function (err, ans) {
console.log("Result: " + ans);
client.end();
fut.return(ans);
});
});
return fut.wait();
};
var result = callR(cmd);
this.response.writeHead(200, {'Content-Type': 'application/json'});
this.response.end(JSON.stringify(result));
}
});

Related

Node.JS returning undefined, despite explicit return statement

I have this simple piece of code:
var http = require('http'), fs = require("fs");
function get(p) {
fs.readFile('.' + p,'utf8', function (err, cont) {
if (err) return "EERRRORRRRR";
else return cont;
})
}
http.createServer(function (request, response) {
var path = ((request.url==="/")?"/index.html":request.url);
console.log(get(path));
}).listen(80);
When I run and connect to the server, it logs undefined...
When I add a "console.log(cont)" like:
fs.readFile('.' + p,'utf8', function (err, cont) {
console.log(html)
if (err) return "EERRRORRRRR";
else return cont;
})
; it logs the correct contents, so why is the function returning undefined? the contents exists...
How would i fix this issue?
The originally context of the code was a simple web server, if you couldn't tell.
Read about callbacks and asynchronous functions, you can find docs in google
var http = require('http'),
fs = require("fs");
// notice new parameter callback
function get(p, callback) {
fs.readFile('.' + p,'utf8', callback);
}
http.createServer(function (request, response) {
var path = ((request.url==="/")?"/index.html":request.url);
// get accepts callback
get(path, function(err, data){
if(err){
response.send('not found');
} else {
response.send(data);
}
});
}).listen(80); // notice: port 80 requires sudo to run, use better 3000
readFile in node.js is async (as well as almost all other functions). You can't return values from async functions, instead, you need to use a callback function that will be called once the operation ends:
fs.readFile('.' + p,'utf8', function (err, cont) {
console.log(html)
if (err) return "EERRRORRRRR";
else handleResponse(cont);
})
function handleResponse(data){//Do something here}
Use readFileSync if you want to return something without having to use a callback.
function get(p) {
var file = fs.readFileSync('.' + p,'utf8');
return file ? file : "EERRRORRRRR";
}
This assumes you don't mind using synchronous/blocking code.

node.js TypeError: Path must be a string. Received null

I was recently working on a javascript that uses socket.io,node.js and express.js.
here is the server code.
var
port = +process.argv[2] || 8080,
sanitizer = require('validator').sanitize,
express = require('express'),
server = express.createServer(),
io = require('socket.io').listen(server),
chat = io.of('/chat'),
canvas = io.of('/canvas')
;
function sanitize(string) {
return sanitizer(string).entityDecode()
}
server.listen(port);
server.get(/(^\/.*$)/, function(request, response) {
var fileName = request.params[0];
if (fileName == '/')
fileName = '/index.html';
response.sendfile(__dirname + '/client' + fileName);
});
io.sockets.on('connection', function(socket) {
socket.on('setName', function (name) {
name = sanitize(name);
socket.set('name', name);
socket.broadcast.emit('receive', {
sender:'Server',
message:name + ' has joined.'
})
});
socket.on('send', function (message) {
socket.get('name', function(error, name) {
if (name)
socket.broadcast.emit('receive', {
sender:name,
message:sanitize(message)
})
})
});
socket.on('draw', function (command) {
io.sockets.emit('draw', command)
});
socket.on('updateCursor', function(position) {
socket.get('name', function(error, name) {
if (name)
socket.broadcast.emit('updateCursor', {
name:name,
position:position
});
});
});
socket.on('disconnect', function() {
socket.get('name', function(error, name) {
if (name) {
socket.broadcast.emit('receive', {
sender:'Server',
message:name + ' has left.'
});
socket.broadcast.emit('removeCursor', name);
}
})
});
});
I am able to set up the server successfully, but when I use the localhost on the browser, I get the following error:
TypeError: Path must be a string. Received null
at assertPath (path.js:8:11)
at win32.join (path.js:221:5)
at exports.send (C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\node_modules\express\node_modules\connect\lib\middleware\static.js:127:20)
at ServerResponse.res.sendfile (C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\node_modules\express\lib\response.js:186:3)
at C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\server.js:23:14
at callbacks (C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\node_modules\express\lib\router\index.js:272:11)
at param (C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\node_modules\express\lib\router\index.js:246:11)
at pass (C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\node_modules\express\lib\router\index.js:253:5)
at Router._dispatch (C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\node_modules\express\lib\router\index.js:280:5)
at Object.Router.middleware [as handle] (C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\node_modules\express\lib\router\index.js:45:10)
I had developed this using the unstable v0.5.8 windows binary version of node.
but I am currently running the 4.4.5 version.
Please help me, where did I go wrong?
It's a little difficult to tell from the sample code you've pasted because your stack trace references line 45 in your index.js as the source of the problem (C:\Users\Akshay\Desktop\whiteboard-master\whiteboard-master\node_modules\express\lib\router\index.js:45:10) but when I look at your sample/pasted code in an IDE, it's not doing anything there. The only call to sendfile (referenced further up in your stack trace) is earlier in the script.
Assuming that's the location of your issue, you should console.log() or otherwise debug the value of the path/filename you're attempting to send to the visitor. Note that in Windows, path delimiters are \ not /. You should use path.join() to form the final path to the file served. The ExpressJS examples illustrate this:
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'index.html'));
});

Stream read returning empty in nodejs

I am using express to create a webservice that will read string data from a stream, and respond to the HTTP POST request with that value. Here is the code for the S3Store.js file that defines the readFileFromS3(.) function:
S3Store.js
S3Store.prototype.readFileFromS3 = function(filename, callback) {
var readConfig = {
'Bucket': 'shubham-test',
'Key': filename
};
var readStream = this.s3.getObject(readConfig).createReadStream();
var allData = '';
readStream.on('data', function(data) {
//data = Buffer.concat([allData, data]);
data = allData + data;
console.log("data: " + data);
});
readStream.on('error', function(err) {
callback(err, null);
});
Now, if I call this method from a terminal like this:
s3Instance.readFileFromS3('123.json', function(err, data) {
console.log(data);
});
I see the appropriate string for data logged to the console. However, when I call the same method from inside one of the routes in express for HTTP POST requests, the service responds with a value of data set to empty string. Code for the POST request:
router.post('/resolve', function(req, res) {
var commandJson = req.body;
var appId = commandJson['appId'];
var command = commandJson['text'];
if (appId == undefined || command == undefined) {
res.status(400).send("Malformed Request: appId: " + appId + ", command: " + command);
};
s3Store.readFileFromS3('123.json', function(err, data) {
res.send(data);
});
});
Why does it return an empty string when calling the readFileFromS3(.) from the HTTP POST method and not when I ran the same method directly from the node console?
You're logging the data but you're not passing anything to the completion callback (see below for some more explanation):
S3Store.prototype.readFileFromS3 = function(filename, callback) {
var readConfig = {
'Bucket': 'shubham-test',
'Key': filename
};
var readStream = this.s3.getObject(readConfig).createReadStream();
var allData = [];
// Keep collecting data.
readStream.on('data', function(data) {
allData.push(data);
});
// Done reading, concatenate and pass to completion callback.
readStream.on('end', function() {
callback(null, Buffer.concat(allData));
});
// Handle any stream errors.
readStream.on('error', function(err) {
callback(err, null);
});
};
I took the liberty to rewrite the data collection to use Buffer's instead of strings, but this obviously isn't a requirement.
The callback argument is a completion function, meant to be called when either reading the S3 stream is done, or when it has thrown an error. The error handling was already in place, but not the part where you would call back when all the data from the stream was read, which is why I added the end handler.
At that point, the readStream is exhausted (everything from it has been read into allData), and you call the completion callback when the collected data as second argument.
The common idiom throughout Node is that completion callbacks take (at least) two arguments: the first is either an error, or null when there aren't errors, and the second is the data you want to pass back to the caller (in your case, the anonymous function in your route handler that calls res.send()).

response is in junked format in node.js

I am developing an API using Node.js. In my application when i hit an URL though browser i gto JSON response in my browser perfectly.. But when i get the response through my node.js code, its coming as junk.
Consider i am hitting the following url in browser:
localhost:2000/xxxxx/rrrrr/ggggg
I am receiving perfect output.
The following is the node.js code:
proxyReq.on("response", function(proxyRes) {
var body = ''
try{
proxyRes.on("data", function(chunk) { //Capture API response here---revisit
body += new Buffer(chunk, 'binary').toString();
//console.log("cccc=" +chunk)
/*zlib.unzip(chunk.toString(), function(err, chunk){
console.log("Inside zliib");
if (!err){
console.log('Response'+chunk.toString())
} else {
console.log("Inside zlib error");
}
});*/
//body = chunk.toString();
//console.log(chunk.toString('utf-8'));
console.log('cccccccc=' +body);
});
}catch(err){
console.log("errrr=" +err.stack);
}
}
Here the 'body' is printing as some junked data. I tried for "utf-8" and "binar" nothing works. Help me to solve this. Thanks in advance.
Mu Junked Data:
Ys��ǿJ����}񛃉/c�؎}�%�ld� a�T��mIL�Z� ]�T��I�r����ߕ�h4��ϕ�7t{�Q4��8���\�L�N؛T��VWM�r
?W&Q��N&/Q����|�W9??W��t�b�n:t>:��(t��G��K��w��=��r\���_��W�N�c��{���u�ۺU���m�^���z�'�ǫ��LFQ�uc���s�>��f
I didn't install using npm:
Third party code regarding ProxyReq:
var ended, mod, proxyReq, req_options;
if (err) {
return _this.error(err, req, res);
}
mod = req.api.data.protocol === "https" ? https : http;
req_options = _this.getHttpProxyOptions(req);
req_options.agent || (req_options.agent = new mod.Agent({
maxSockets: 100,
rejectUnauthorized: req.api.data.strictSSL
}));
_this.logger.debug(("Backend: " + req_options.method + " to ") + ("'" + req.api.data.protocol + "://") + ("" + req_options.host + ":" + req_options.port + req_options.path));
proxyReq = mod.request(req_options);
proxyReq.setTimeout(req.api.data.endPointTimeout * 1000, function() {
var e;
e = new Error("ETIMEDOUT");
e.code = "ETIMEDOUT";
proxyReq.emit("error", e);
return proxyReq.abort();
});
ended = false;
I found your problem. This is the documentation of event response for http/https
Emitted when a response is received to this request. This event is emitted only once. The response argument will be an instance of http.IncomingMessage.
And http.IncomingMessage is a ReadableStream.
After that, you should use this two events of stream to convert this into String :
Event: 'data'
Event: 'end'
For stream.on('data', ...) you should collect your data data into either a Buffer (if it is binary) or into a string.
For on('end', ...) you should call a callback with you completed buffer, or if you can inline it and use return using a Promises library.
Example, for you, you can change your proxyReq.on("response") callback content by this :
var Buffer = require('buffer').Buffer;
proxyReq.on("response", function(chunk) {
//proxyRes.setEncoding ('utf8');
var body = '';
chunk.on('data', function(data) {
body += new Buffer(data, 'binary').toString();
});
chunk.on('end', function () {
// callback
});
chunk.on('error', function (err) {
// catchable error
});
});
To simplify you request part, I recommand you to use the node-request package : https://github.com/mikeal/request

Callback for Async.Map Not Working

I'm relatively new to callbacks and have been unsuccessful in getting the following code to work. I have used the async.map function to return the data from each web call to its respective page. However, my console.log(return) is returning [ , undefined] even though the console.log(data) prior to the callback(data) is returning the web page's html. Here's my code:
var http = require("http"),
fs = require("fs"),
page, chap, ot,
async = require("async");
ot = fs.open('ot.txt', 'w');
page = "test";
chap = 2;
function getData(url, callback) {
var data = "";
var options = {
host: "rs.au.com",
port: 80
}
options.path = url;
console.log("request sent to: http://" + options.host + options.path);
var req = http.request(options, function(res) {
console.log("Response received " + res.statusCode);
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function(e) {
console.log(data);
callback(e, data);
});
}).end();
}
function main() {
var pathArr = [];
for ( var i = 1; i <= chap; i++ ) {
pathArr[i] = "/".concat(page, "/", i, ".html");
}
async.map(pathArr, getData, function(err, result) {
console.log("The result is :" + result);
});
}
main();
Could anyone please point out why my code is not working and how I can correct it?
Much appreciated!
EDIT: After Brandon Tilley's response I amended the callback function from callback(data) to callback(e, data), however I'm getting no response now from the last console.log output.
The Async library assumes your callbacks adhere to the standard Node.js callback signature, which is callback(err, others...). Since you are passing data as the first argument, Async assumes it's an error. You should use callback(e, data) instead (since e will be null in the case of no errors).
[Update]
The other problem is that your array is not correct. Since i starts at 1 and goes up to chap, pathArr[0] is undefined. Change:
pathArr[i] = "/".concat(page, "/", i, ".html");
to
pathArr[i-1] = "/".concat(page, "/", i, ".html");

Resources