0
votes

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

phpStorm debug screenshot of test

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.

enter image description here

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.

1
Should have been a comment: moving it here: unzip (the function) does not have an stdout or stderr, so self.stdout(data); and self.stderr(data); will both fail with an error like: self.stderr(data); ^ TypeError: Object # has no method 'stderr' You should probably change these to console.log/error, or something along those lines. Also, the unzip.on('close'... handler will be called even if there is data sent to unzip.stderr, so no need to call the callback from inside the stderr handler. If this is not the issue, please post the error message you are getting out of the web service.Jonathan Wiepert
Are you using some framework to start it as a web service?Jonathan Wiepert
No, just using http.createServer(), the handler that gets passed calls the unzip "method". My API is very simple so just using my own functions to handle HTTP requests.kierans

1 Answers

1
votes

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.