NodeJS not spawning child process except in tests - node.js

I have the following NodeJS code:
var spawn = require('child_process').spawn;
var Unzipper = {
unzip: function(src, dest, callback) {
var self = this;
if (!fs.existsSync(dest)) {
fs.mkdir(dest);
}
var unzip = spawn('unzip', [ src, '-d', dest ]);
unzip.stdout.on('data', function (data) {
self.stdout(data);
});
unzip.stderr.on('data', function (data) {
self.stderr(data);
callback({message: "There was an error executing an unzip process"});
});
unzip.on('close', function() {
callback();
});
}
};
I have a NodeUnit test that executes successfully. Using phpStorm to debug the test the var unzip is assigned correctly
However if I run the same code as part of a web service, the spawn call doesn't return properly and the server crashes on trying to attach an on handler to the nonexistent stdout property of the unzip var.
I've tried running the program outside of phpStorm, however it crashes on the command line as well for the same reason. I'm suspecting it's a permissions issue that the tests don't have to deal with. A web server spawning processes could cause chaos in a production environment, therefore some extra permissions might be needed, but I haven't been able to find (or I've missed) documentation to support my hypothesis.
I'm running v0.10.3 on OSX Snow Leopard (via MacPorts).
Why can't I spawn the child process correctly?
UPDATES
For #jonathan-wiepert
I'm using Prototypical inheritance so when I create an "instance" of Unzipper I set stdout and stderr ie:
var unzipper = Unzipper.spawn({
stdout: function(data) { util.puts(data); },
stderr: function(data) { util.puts(data); }
});
This is similar to the concept of "constructor injection". As for your other points, thanks for the tips.
The error I'm getting is:
project/src/Unzipper.js:15
unzip.stdout.on('data', function (data) {
^
TypeError: Cannot call method 'on' of undefined
As per my debugging screenshots, the object that is returned from the spawn call is different under different circumstances. My test passes (it checks that a ZIP can be unzipped correctly) so the problem occurs when running this code as a web service.

The problem was that the spawn method created on the Object prototype (see this article on Protypical inheritance) was causing the child_process.spawn function to be replaced, so the wrong function was being called.
I saved child_process.spawn into a property on the Unzipper "class" before it gets clobbered and use that property instead.

Related

Can't seem to figure out how to get the output of an process fired with spawn. stdout.on('data') not outputting for me

I am trying to setup some automation on a game server for the game Rust.
The game server itself is ran by running its executable file RustDedicated.exe with some arguments.
According to some googling and reading here on Stack Overflow I have made this script:
import config from "config";
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
const GAMESERVERPATH: string = config.get("Environment.RustDedicatedPath");
const EXECUTABLE: string = config.get("Environment.ExecutableFile");
const GAMESERVERARGS: Array<string> = [
"-batchmode",
"+server.port", `${config.get("Server.port")}`,
"+server.level", `"${config.get("Server.level")}"`,
"+server.seed", `${config.get("Server.seed")}`,
"+server.worldsize", `${config.get("Server.worldsize")}`,
"+server.maxplayers", `${config.get("Server.maxplayers")}`,
"+server.hostname", `"${config.get("Server.hostname")}"`,
"+server.description", `"${config.get("Server.description")}"`,
"+server.headerimage", `"${config.get("Server.headerimage")}"`,
"+rcon.port", `${config.get("Rcon.port")}`,
"+rcon.password", `"${config.get("Rcon.password")}"`,
"+rcon.web", `${config.get("Rcon.web")}`
];
const gameServerProc : ChildProcessWithoutNullStreams = spawn(
GAMESERVERPATH+EXECUTABLE,
GAMESERVERARGS,
{
cwd: GAMESERVERPATH,
shell: true,
}
);
gameServerProc.stdout.on("data", (data) => {
console.log(`stdout:${data.toString()}`);
});
gameServerProc.stderr.on("data", (data) => {
console.log(`stderr:${data.toString()}`);
});
gameServerProc.on("error", (err) => {
console.log(`error:${err.message}`);
});
What is happening is that i can see the output of the executable in the terminal window, and the server is firing without errors, but it seems that stdout is not firing the on('data') event.
I never see stdout:.
See the screenshot below where i have Code open, the output is on the bottom right.
Why is my script failing to get the on('data') firing when the executable outputs?
This issue had nothing to do with the code, but rather the game engine Unity. An extra argument -logFile - had to be present for the executable to output to stdout.

Node PhantomJS script onResourceError path issue

Having some trouble using the webpage API in a phantomJS script I'm using for load testing.
I'm running the script in a child process, like so:
var path = require('path');
var childProcess = require('child_process');
var binPath = require('phantomjs').path;
var childArgs = [
path.join(__dirname, 'phantom-script.js')
];
var spawn = childProcess.spawn;
var child = spawn(binPath, childArgs);
child.stdout.on('data', function(data) {
const buf = Buffer.from(data);
console.log('stdout:', buf.toString());
});
child.stderr.on('data', function(data) {
const buf = Buffer.from(data);
console.log('stderr:', buf.toString());
});
And my simple phantomJS script:
var webPage = require('webpage');
var page = webPage.create();
page.onConsoleMessage = function (msg) {
console.log(msg);
};
page.onResourceError = function(resourceError) {
console.log(resourceError.errorCode + ':', resourceError.errorString);
};
function runScript() {
page.open('<webpage-url>', function(status) {
console.log('Status:', status);
if (status === 'success') {
page.evaluate(function() {
console.log('Title:', document.title);
});
}
});
}
runScript();
So to start the phantomJS script, if both of these files are in the test/ directory, and my current directory is up one from that: node test/child-process.js, which then spawns the child process and runs my phantomJS script.
So, this gets the script to run, but it always fails in page.open because of a resource error. Replacing my url with Google's, or really any website, works fine.
The error logged in onResourceError is stdout: 202: Cannot open file:///Users/<user>/path/to/local/current/directory: Path is a directory.
This is always the path from which I'm running this script. If I move down a directory into test/ and run it with node child-process.js, the error instead logs that directory.
As a headless browser, I assumed phantomJS would interface with a webpage like any client would, just without rendering the template--what does the current directory from which the script was run have anything to do with opening the webpage? Why would it be trying to load resources from my local directory when the webpage URL points to a public website, hosted at the IP and PORT specified in the first argument of page.open (e.g. xx.xxx.xx.xx:PORT)?
I'm at a bit of a loss here. The phantomJS path and all that is correct, since it runs the script fine. I just don't understand why page.open would attempt to open the directory from which the script was called--what does that have to do with its function, which is to open the URL and load it to the page?
Not sure if this is even worthy of answering--as opposed to just deleting.
I figured it out when I manually typed in the argument www.google.com, instead of copy/pasting from the browser, and and I got this as the path in the error: file:///Users/<user>/path/to/local/current/directory/www.google.com.
Now I know why I couldn't find a SO question for it. A stupid error on my part at any rate, it would've been a quick debug if the error had appended the IP address and PORT (my "url") to the end of the file path like it did for www.google.com, a clear indicator that it's not pinging a URL.
TL;DR: It's a URL, you need http(s)://...

nodejs open chrome in window with arguments

Trying to write my own plugins for gulp. So I've written a gulp task that attempts opens the chrome browser in windows (i'll work on getting working for mac/linux later).
It seems to work except it's not passing in my arguments:
/*
* Open
*/
gulp.task('open', function (done) {
var uri = 'http://localhost:' + CONFIG.PORT,
CONFIG.PORT = 8080,
args = [
uri,
'--no-first-run',
'--no-default-browser-check',
'--disable-translate',
'--disable-default-apps',
'--disable-popup-blocking',
'--disable-zero-browsers-open-for-tests',
'--disable-web-security',
'--new-window',
'--user-data-dir="C:/temp-chrome-eng"'
]
cp.spawn('C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', args);
})
How do i get it to accept my arguments it passed in? Am in providing it the wrong arguments?
I would recommend using a quite popular npm module, opener, instead which will solve both your issue with arguments and cross platform support.
Instead of finding the browser executable like you are doing, you can simply write:
var opener = require('opener')
opener('http://google.com')
If you however want to go with your current method, try capturing the output by naming your process and then listening on stderr and stdout:
var chrome = cp.spawn ...
chrome.stdout.on('data', function (data) {
console.log(data.toString())
})
chrome.stderr.on('data', function (data) {
console.error(data.toString())
})
It does work for me on linux if I replace your chrome path with chromium.

Using grunt to run a node server and do cleanup after

So basically this is what I want to do. Have a grunt script that compiles my coffee files to JS. Then run the node server and then, either after the server closes or while it's still running, delete the JS files that were the result of the compilation and only keep the .coffee ones.
I'm having a couple of issues getting it to work. Most importantly, the way I'm currently doing it is this:
grunt.loadNpmTasks("grunt-contrib-coffee");
grunt.registerTask("node", "Starting node server", function () {
var done = this.async();
console.log("test");
var sp = grunt.util.spawn({
cmd: "node",
args: ["index"]
}, function (err, res, code) {
console.log(err, res, code);
done();
});
});
grunt.registerTask("default", ["coffee", "node"]);
The problem here is that the node serer isn't run in the same process as grunt. This matters because I can't just CTRL-C once to terminate JUST the node server.
Ideally, I'd like to have it run in the same process and have the grunt script pause while it's waiting for me to CTRL-C the server. Then, after it's finished, I want grunt to remove the said files.
How can I achieve this?
Edit: Note that the snippet doesn't have the actual removal implemented since I can't get this to work.
If you keep the variable sp in a more global scope, you can define a task node:kill that simply checks whether sp === null (or similar), and if not, does sp.kill(). Then you can simply run the node:kill task after your testing task. You could additionally invoke a separate task that just deletes the generated JS files.
For something similar I used grunt-shell-spawn in conjunction with a shutdown listener.
In your grunt initConfig:
shell: {
runSuperCoolJavaServer:{
command:'java -jar mysupercoolserver.jar',
options: {
async:true //spawn it instead!
}
}
},
Then outside of initConfig, you can set up a listener for when the user ctrl+c's out of your grunt task:
grunt.registerTask("superCoolServerShutdownListener",function(step){
var name = this.name;
if (step === 'exit') process.exit();
else {
process.on("SIGINT",function(){
grunt.log.writeln("").writeln("Shutting down super cool server...");
grunt.task.run(["shell:runSuperCoolJavaServer:kill"]); //the key!
grunt.task.current.async()();
});
}
});
Finally, register the tasks
grunt.registerTask('serverWithKill', [
'runSuperCoolJavaServer',
'superCoolServerShutdownListener']
);

Spawned phantomjs process hanging

I'm trying to create a node server that spawns phantomjs processes to create screenshots. The grab.js script works fine when executed and I've confirmed that it writes to stdout. Problem is the node code that spawns the process simply hangs. I've confirmed that phantomjs is in the path. Anyone know what might be happening here or how I might troubleshoot this?
Here's the phantomjs code (grab.js) that renders the page and writes the data to stdout:
var page = require('webpage').create(),
system = require('system'),
fs = require('fs');
var url = system.args[1] || 'google.com';
page.viewportSize = {
width: 1024,
height: 1200
};
page.open(url, function() {
var b64 = page.renderBase64('png');
fs.write('/dev/stdout', b64, 'w');
phantom.exit();
});
And here's the node code that spawns the phantom progress and prints the result (hangs):
var http = require('http'),
exec = require('child_process').exec,
fs = require('fs');
exec('phantomjs grab.js google.com', function(error, stdout, stderr) {
console.log(error, stdout, stderr);
});
I have had similar issues with exec and then switched to using spawn instead and it worked.
According to this article , Use spawn when you want the child process to return huge binary data to Node, use exec when you want the child process to return simple status messages.
hth
I had same problem, in my case it was not in nodejs, but in phantomjs (v2.1).
It's known problem when phantom`s open method hangs.
Also, found second link (I guess same person wrote) in which author points that requestAnimationFrame is not working well with tweenJs, which causes freezing. PhantomJS returns unixtimestamp but tweenjs expects it to be DOMHighResTimeStamp, and so on...
Trick is to inject request-animation-frame.js (which is also provided in that article)

Resources