node js :Can\'t set headers after they are sent - javascript

How to send the response properly. I like to move to another page after FTP and Telnet sessions.
Even tried using res.redirect function too. While using res.redirect, i commented window.location= "SaveRedirect"; line, in javascript of html page. Even then, I get the same error.
Here presenting the code snippet,
the javascript code in html
IPData ={
"DeviceIpAddr": "192.168.1.2"
};
function processSave() {
$.post("PostSave", IPData, function(data) {
window.location= "SaveRedirect";
});
return true;
}
node js code snippet
app.get('/SaveRedirect', function (req, res) {
res.sendFile( __dirname + "/" + "Page2.htm" );
})
var bodyParser = require('body-parser');
var testJsonEncoder = bodyParser.json();
urlEncoded = bodyParser.urlencoded({ extended: true });
app.post('/PostSave', testJsonEncoder,urlEncoded, function(request, response){
var LoginData = JSON.stringify(request.body, null, 4);
console.log(LoginData);
FormDataObj = JSON.parse(LoginData);
gDeviceIpAddress = FormDataObj.DeviceIpAddr;
console.log("IP Address" + gDeviceIpAddress);
console.log("FTP to linux box...");
ftpFile(gDeviceIpAddress, "PUT", "temp_data.json", response);
console.log("FTP temp_data file to linux box... Done!");
TelnetLinux(gDeviceIpAddress);
console.log("Telnet to linux box... Done!");
response.send("just to send response");
//response.json(LoginData);
});
function ftpFile(ftpIPAddr, operation, filename, resp) {
console.log("ftpIPAddr = " + ftpIPAddr);
ftpConfig.host = ftpIPAddr;
// Handle error
ftpClient.on("error", function (err) {
console.log(" - ERROR HANDLE - \n");
console.log(err);
resp.send("FAIL: " + err.message);
ftpClient.end();
});
// Handle error
ftpClient.on("ready", function () {
console.log("Connected....\n");
if(operation == "GET") {
console.log("filename : " + filename);
ftpClient.get(filename, function(err, stream) {
if (err)
{
resp.send("FAIL: " + err.message + " - " + filename);
ftpClient.end();
//throw err;
}
else {
stream.once('close', function() { ftpClient.end(); });
stream.pipe(fs.createWriteStream('test_data.json'));
resp.send("SUCCESS: file received");
ftpClient.end();
}
});
}
if(operation == "PUT") {
ftpClient.put(filename, filename, function(err) {
if (err) {
resp.send("FAIL: " + err.message + " - " + filename);
ftpclient.end();
throw err;
}
else {
resp.send("SUCCESS: file sent");
ftpClient.end();
}
});
}
});
ftpClient.connect(ftpConfig);
}
function TelnetLinux(deviceIPAddr) {
var telnetParams = {
host: deviceIPAddr,
port: 23,
username: "webtest",
password: "test123",
shellPrompt: '$',
timeout: 1500,
// removeEcho: 4
};
cmd = "ls";
console.log("Telnet Address : " + deviceIPAddr);
telnetClient.on('ready', function(prompt) {
telnetClient.exec(cmd, function(err, response) {
//console.log(response);
console.log("Cmd executed !");
});
});
telnetClient.on('timeout', function() {
console.log('telnet socket timeout!')
telnetClient.end();
});
telnetClient.on('close', function() {
console.log('telnet connection closed');
});
telnetClient.connect(telnetParams);
}
Error :
c:\WebTest>node webapp.js
Example app listening at http://:::8081
IP Address192.168.1.2
FTP to linux box...
ftpIPAddr = 192.168.1.2
FTP temp_data file to linux box... Done!
Telnet Address : 192.168.1.2
Telnet to linux box... Done!
Cmd executed !
telnet socket timeout!
telnet connection closed
Connected....
_http_outgoing.js:346
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:346:11)
at ServerResponse.header (c:\WebTest\node_modules\express\lib\response.js:719:10)
at ServerResponse.send (c:\WebTest\node_modules\express\lib\response.js:164:12)
at c:\WebTest\webapp.js:222:11
at Object.cb (c:\WebTest\node_modules\ftp\lib\connection.js:1017:13)
at Parser.<anonymous> (c:\WebTest\node_modules\ftp\lib\connection.js:117:20)
at emitTwo (events.js:100:13)
at Parser.emit (events.js:185:7)
at Parser._write (c:\WebTest\node_modules\ftp\lib\parser.js:59:10)
at doWrite (_stream_writable.js:301:12)
at writeOrBuffer (_stream_writable.js:287:5)
at Parser.Writable.write (_stream_writable.js:215:11)
at Socket.ondata (c:\WebTest\node_modules\ftp\lib\connection.js:273:20)
at emitOne (events.js:90:13)
at Socket.emit (events.js:182:7)
at readableAddChunk (_stream_readable.js:153:18)

This happens when you try to do res.send (or response.send) multiple times in the same call. I believe it has to do with this bit of code:
app.post('/PostSave', testJsonEncoder, urlEncoded, function(request, response) {
var LoginData = JSON.stringify(request.body, null, 4);
console.log(LoginData);
FormDataObj = JSON.parse(LoginData);
gDeviceIpAddress = FormDataObj.DeviceIpAddr;
console.log("IP Address" + gDeviceIpAddress);
console.log("FTP to linux box...");
ftpFile(gDeviceIpAddress, "PUT", "temp_data.json", response);
console.log("FTP temp_data file to linux box... Done!");
TelnetLinux(gDeviceIpAddress);
console.log("Telnet to linux box... Done!");
response.send("just to send response");
//response.json(LoginData);
});
You're doing a response.send at the end of this function, and again in ftpFile()

Since you are sending response multiple times in app.post("/Postsave" and function ftpFile for the same request, you get this error.
To resolve this, I would suggest you to send the result(or error) from the ftpfile back to the app.post(i.e., through callback) and then based on the result or error send the response from the app.post function.

Related

AWS Lambda: Error: getaddrinfo ENOTFOUND from api

I have created the following code which grabs a seralizedXmlFile object from an S3 bucket and pushes it to a api service. This returns FAIL with the logs showing
Error: getaddrinfo ENOTFOUND http://url
at errnoException (dns.js:28:10)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:76:26)
CODE:
const AWS = require('aws-sdk');
const https = require('http');
var s3 = new AWS.S3();
var un;
var pw;
var seralizedXmlFile;
let index = function index(event, context, callback) {
//for testing I have named the bucket and key
var params = {
Bucket: "bucket", //event.bucketName,
Key: "personnelData_50112404_635705766849654385.xml" //event.fileName
};
s3.getObject(params, function(data, err)
{
if (data)
{
let seralizedXmlFile = err.Body.toString('utf-8'); // Use the encoding necessary
console.log("objectData " + seralizedXmlFile);
}
});
var ssm = new AWS.SSM({region: 'ap-southeast-2'});
var paramsx = {
'Names' : ['/App/ServiceUsername', '/App/ServicePassword'],
'WithDecryption' : true
};
ssm.getParameters(paramsx, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else {console.log('data: ' + JSON.stringify(data)); // successful response
console.log('password: ' + data.Parameters[0].Value);
console.log('username: ' + data.Parameters[1].Value);
pw = data.Parameters[0].Value;
un = data.Parameters[1].Value;
}
const req = https.request('http:/url/api/SyncPersonnelViaAwsApi/Get/5', (res) => {
res.headers + 'Authorization: Basic ' + un + ':' + pw;
let body = seralizedXmlFile;
console.log('Status:', res.statusCode);
console.log('Headers:', JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
console.log('Successfully processed HTTPS response');
console.log('returned res: ' + res);
callback(null, res);
});
});
req.end();
});
};
exports.handler = index;
I followed a Q and A I found on AWS Lambda: Error: getaddrinfo ENOTFOUND
and changed the code to
var params = {
host: "http://URL",
path: "/api/SyncPersonnelViaAwsApi/Get/5"
};
var req = https.request(params, function(res) {
let data = '';
console.log('STATUS: ' + res.statusCode);
res.setEncoding('utf8');
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
console.log("DONE");
console.log(JSON.parse(data));
but again firing the same error....does anyone have any idea what the issue is?
I have also tested the web service api through POSTMAN, so I can confirm it is working
Thank You
Try
host: "URL",
instead of
host: "http://URL",
Also, you're using the https library and your URL prefix is http:// but I think you can/should omit it altogether.
Error: getaddrinfo ENOTFOUND http://url
means client was not able to connect to given address. Please try specifying host without http:
var params = {
host: "URL",
path: "/api/SyncPersonnelViaAwsApi/Get/5"
};

Loopback/Strongloop failed with ssh2 function callback Error: write ECONNABORTED

I want to have a REST API that would return in web browser some cisco router CLI output from Cisco CLI command inputs via REST request using ssh2 and loopback.
I can't figure it out because of these errors.
The code below will run a few milliseconds returning some correct and expected output but then breaks the loopback instance at server side will break/stop.
Error encountered:
C:\LOCAL_APPS\Loopback\filedir\node_modules\async\dist\async.js:955
if (fn === null) throw new Error("Callback was already called.");
^
Error: Callback was already called.
at C:\LOCAL_APPS\Loopback\filedir\node_modules\async\dist\async.js:955:32
at C:\LOCAL_APPS\Loopback\filedir\node_modules\async\dist\async.js:3871:13
at interceptInvocationErrors
(C:\LOCAL_APPS\Loopback\filedir\node_modules\strong-remoting\lib\remote-
objects.js:713:22)
at C:\LOCAL_APPS\Loopback\filedir\node_modules\loopback-phase\node_modules\async\lib\async.js:154:25
at C:\LOCAL_APPS\Loopback\filedir\node_modules\loopback-phase\node_modules\async\lib\async.js:154:25
at C:\LOCAL_APPS\Loopback\filedir\node_modules\loopback-phase\node_modules\async\lib\async.js:154:25
at C:\LOCAL_APPS\Loopback\filedir\node_modules\strong-remoting\lib\remote-objects.js:679:7
at C:\LOCAL_APPS\Loopback\filedir\node_modules\strong-remoting\lib\http-context.js:305:7
at callback (C:\LOCAL_APPS\Loopback\filedir\node_modules\strong-remoting\lib\shared-method.js:
Stream :: close :: code: 0, signal: undefined
events.js:183
throw er; // Unhandled 'error' event
^
Error: write ECONNABORTED
at _errnoException (util.js:1024:11)
at Socket._writeGeneric (net.js:767:25)
at Socket._write (net.js:786:8)
at doWrite (_stream_writable.js:387:12)
at writeOrBuffer (_stream_writable.js:373:5)
at Socket.Writable.write (_stream_writable.js:290:11)
at Socket.write (net.js:704:40)
at SSH2Stream.ondata (_stream_readable.js:639:20)
Under common/models/device.js:
'use strict';
module.exports = function(Device) {
Device.getIPInterfaceBrief = function(ipaddr,cb){
var filter = {
include:{
relation: 'infosheet',
scope:{
fields:['data']
}
}
}
var Client = require('ssh2').Client;
var conn = new Client();
conn.on('ready', function() {
console.log('Client :: ready');
conn.exec('show ip int br', function(err, stream) {
if (err) throw err;
stream.on('close', function(code, signal) {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
conn.end();
}).on('data', function(data) {
cb(null,"data: " + 'done') //or cb(null,"data: " + data)
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
});
});
}).connect({
host: ipaddr,
port: 22,
username: 'routerusername',
password: 'passwordhere'
});
console.log("check 1");
};
Device.remoteMethod('getIPInterfaceBrief',{
description: "Returns the brief interface information of a device",
accepts:{
arg:'ipaddr',
type: 'string',
required: false
},
http:{
path:'/:ipaddr/getIPInterfaceBrief',
verb: 'get'
},
returns:{
arg:'interfaceinfo',
type:'any'
}
});
}; //module.exports
REST TEST URL:
http:// localhost:3000/api/adevices/some-ip-here/getIPInterfaceBrief
Error encountered after few seconds API responded the get REST request:
I temporarily added some try catch statement it worked somehow.
I think loopback is not part of the issue.
Hope you have better fix since the code works on other machines like Linux system but for Cisco routers have some problem with the line "conn.end".
Added try and catch to capture the errors..
if (err) throw err;
stream.on('close', function(code, signal) {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
try {
conn.end();
}catch (err){
console.log("error occured",err);
}
}).on('data', function(data) {
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
});
My test did capture the error see here

i want to send a CTRL + C command to client how can i acheive this in NodeJS?

My Code Looks Like this
var Client = require('ssh2').Client;
var fs=require('fs');
var conn = new Client();
conn.on('ready', function() {
console.log('Client :: ready');
conn.shell('xtail', function(err, stream) {
stream.write('xtail\n\r');
if (err) throw err;
stream.on('close', function(code, signal) {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
conn.end();
}).on('data', function(data) {
// stream.stdin.write('?');
console.log('hey');
console.log('STDOUT: ' + data);
fs.appendFile('D:\\Breme\\As1.txt',data,function(err){
if(err)
{
console.log(err);
}
//console.log('file saving..')
});
// setTimeout(function(){
// process.exit(1);
// }, 20000);
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
conn.destroy();
});
});
}).connect({
host: '10.214.14.15',
port: 22,
username: 'bwadn',
password: 'bwain'
});
after connecting to server I am downloading the file but I want to send 'Ctrl+C' command from client, so that it stops receiving data
Can anyone please help me in solving this, As i am new to NodeJS I need your support in solving this...thanks
Most reliable way in this particular case, since you are using a variant of tail in your shell, is to use
stream.write('\x03'); //send ctrl+c
you would need to send it when you know you don't need xtail output anymore. It is beyond the scope of this question to discuss the means, but setTimeout is definitely not a good idea.
Alternative (preferred) method:
stream.signal('INT')
and for other uses see Ssh2 Channel API

display ssh2 stdout and stderr in textarea

I'm building a node.js application to manage servers.
In this application, I can create some shell scripts and execute them on a server by clicking on a button.
Here is what I have in my routes.js file for this action :
app.get('/exec', isLoggedIn, function(req, res) {
var url = require('url');
var url_parts = url.parse(req.url, true);
var query = url_parts.query;
var Server = require('../app/models/server');
var Script = require('../app/models/script');
var async = require('async');
var scriptId = query.scriptId;
var userId = query.userId;
var serverId = query.serverId;
var stdout = "";
var stderr = "";
async.parallel({
servers: function(callback){
Server.find({"_id" : serverId}).exec(callback);
},
scripts: function(callback){
Script.find({"_id" : scriptId}).exec(callback);
}
}, function(err, results) {
if (err)
res.send(err);
var selectedServer = results.servers;
var selectedScript = results.scripts;
var serverIp = selectedServer[0].serverDetails.serverAddress;
var serverPort = selectedServer[0].serverDetails.serverPort;
var serverUsername = selectedServer[0].serverDetails.serverUsername;
var scriptContent = selectedScript[0].scriptDetails.scriptContent;
var Connection = require('ssh2');
var conn = new Connection();
conn.on('ready', function() {
//console.log('Connection :: ready');
conn.exec(scriptContent, function(err, stream) {
if (err) throw err;
stream.on('exit', function(code, signal) {
//console.log('Stream :: exit :: code: ' + code + ', signal: ' + signal);
}).on('close', function() {
//console.log('Stream :: close');
conn.end();
}).on('data', function(data_out) {
console.log('STDOUT: \n' + data_out);
stdout = stdout + data_out;
res.render('execresults.ejs', {
stdout : stdout
});
}).stderr.on('data', function(data_err) {
console.log('STDERR: \n' + data_err);
});
});
}).connect({
host: serverIp,
port: serverPort,
username: serverUsername,
privateKey: require('fs').readFileSync('id_rsa')
});
conn.on('error', function(e) { console.log("Connection failed or timed out")});
});
});
It works fine and I have the results of the execution in the console. But I would like to display both stdout and stderr results in two textarea of an ejs page.
The problem is that when I run a simple command like "uptime", it works, I will have the stdout result displayed in my textarea.
But when the shell script returns both stdout and stderr, or if stdout is a large amount of text, I don't have everything passed to my ejs page because of this error I think : Error: Can't set headers after they are sent.
My question is : How can I send both stdout and stderr to my ejs page, only when the script is completely executed ? I tried to build a "stdout" variable by concatenating all stdout output but it doesn't seem to work...
Can someone help me ?
thanks a lot
Assuming the process eventually ends on its own and it does not output a lot of data, you could probably just buffer the data and then render when the process closes:
conn.exec(scriptContent, function(err, stream) {
if (err)
return res.send(err);
var stdout = '',
stderr = '';
stream.on('close', function() {
conn.end();
res.render('execresults.ejs', {
stdout: stdout,
stderr: stderr
});
}).on('data', function(data) {
stdout += data;
}).stderr.on('data', function(data) {
stderr += data;
});
});
Secondly,
if (err)
res.send(err);
should really have a return:
if (err)
return res.send(err);
to prevent further execution on errors.

nodejs Error: Parse Error at Socket.ondata

I have got some error when use http.request to make a client request(node v0.6.18, v0.6.3), the following code produces the error and I have some questions.
var http = require('http');
var url = require('url');
http.createServer(function(req, res) {
var data = '多情自古空余恨';
res.writeHead(200, {
'Content-Type': 'text/plain',
'Content-Length': 1 //ERROR
});
res.end(data);
}).listen(3000);
function request(options, callback) {
var req = http.request(options, function(res) {
var data = '';
res.setEncoding = 'utf8';
res.on('data', function(chunk) {
data += chunk;
});
res.on('error', function(err) {
callback(new Error('res error: ' + err));
});
res.on('end', function() {
console.log('res on end');
callback(null, data);
});
});
req.on('socket', function(socket) {
socket.on('error', function(err) {
console.log('socket on error');
callback('socket error: ' + err);
req.abort();
});
});
req.end();
}
request({
host: 'localhost',
port: 3000,
method: 'GET'
}, function(err, data) {
console.log('result, err: ' + err + ', data: ' + data);
});
Outputs:
res on end
result, err: null, data: �
socket on error
result, err: socket error: Error: Parse Error, data: undefined
Here are my questions:
Why res's 'end' event happened earlier than socket's 'error' event?
If I do want to callback an error when "Parse Error at Socket.ondata" happens like the above code or in any other situation, how to callback once instead of twice as the above output(IF res's 'end' event really hanppened earlier than socket's 'error' event)?
I need your help! Thanks.
===============================
I found the same code outputs:
res on end
result, err: null, data: �
in node v0.6.6 and v0.6.11. Why?
Because there's a content-length header of 1, when the request receives exactly 1 octet of data, it assumes that's all there is and fires the end callback. After that, more data is received that the socket doesn't know what to do with, so it fires the error.
To work around this, you could perhaps wait a short period before firing the callback for success and track if it's been fired. For example:
var req = http.request(options, function(res) {
var data = '';
res.setEncoding = 'utf8';
res.on('data', function(chunk) {
data += chunk;
});
res.on('error', function(err) {
if(!callback.called) { // check before firing the callback
callback(new Error('res error: ' + err));
callback.called = true; // set after firing the callback
} // ..
});
res.on('end', function() {
process.nextTick(function() { // use setTimeout if nextTick is too short
if(!callback.called) { //..
console.log('res on end');
callback(null, data);
callback.called = true; // ..
} // ..
}); // ..
});
});
req.on('socket', function(socket) {
socket.on('error', function(err) {
if(!callback.called) { // ..
console.log('socket on error');
callback('socket error: ' + err);
callback.called = true; // ..
} // ..
req.abort();
});
});
req.end();
(I tried to add comments after all of the new lines to make them stand out a little.)

Categories

Resources