Skip to content

Commit 1be5e7b

Browse files
committed
Ensure that when using get the consumer always receives feedback
regardless of whether the signal is an `end`, `close` or an `error`. Fixes #106.
1 parent 36b6d8a commit 1be5e7b

File tree

2 files changed

+43
-25
lines changed

2 files changed

+43
-25
lines changed

lib/jsftp.js

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,14 @@ Ftp.prototype.parseResponse = function(data) {
146146
var next = this.cmdBuffer_[0][1];
147147
if (data.isMark) {
148148
// If we receive a Mark and it is not expected, we ignore that command
149-
if (!next.expectsMark || next.expectsMark.marks.indexOf(data.code) === -1)
149+
if (!next.expectsMark || next.expectsMark.marks.indexOf(data.code) === -1) {
150150
return;
151+
}
152+
151153
// We might have to ignore the command that comes after the mark.
152-
if (next.expectsMark.ignore)
154+
if (next.expectsMark.ignore) {
153155
this.ignoreCmdCode = next.expectsMark.ignore;
156+
}
154157
}
155158

156159
if (this.ignoreCmdCode && this.ignoreCmdCode === data.code) {
@@ -300,8 +303,9 @@ Ftp.prototype.auth = function(user, pass, callback) {
300303

301304
function notifyAll(err, res) {
302305
var cb;
303-
while (cb = self.pending.shift())
306+
while (cb = self.pending.shift()) {
304307
cb(err, res);
308+
}
305309
}
306310

307311
if (this.authenticating) return;
@@ -410,26 +414,22 @@ Ftp.prototype.emitProgress = function(data) {
410414
* case the operation was not successful.
411415
*
412416
* @param {String} remotePath File to be retrieved from the FTP server
413-
* @param {String} localPath Local path where the new file will be created
414-
* @param {Function} callback Gets called on either success or failure
417+
* @param {Function|String} localPath Local path where we create the new file
418+
* @param {Function} [callback] Gets called on either success or failure
415419
*/
416420
Ftp.prototype.get = function(remotePath, localPath, callback) {
417421
var self = this;
418-
if (arguments.length === 2) {
419-
callback = once(localPath || NOOP);
420-
this.getGetSocket(remotePath, callback);
422+
var finalCallback;
423+
424+
if (typeof localPath === 'function') {
425+
finalCallback = once(localPath || NOOP);
421426
} else {
422427
callback = once(callback || NOOP);
423-
this.getGetSocket(remotePath, function(err, socket) {
428+
finalCallback = function(err, socket) {
424429
if (err) {
425430
return callback(err);
426431
}
427432

428-
if (!socket) {
429-
return callback(new Error(
430-
'An unknown error occurred when trying to retrieve PASV socket'));
431-
}
432-
433433
var writeStream = fs.createWriteStream(localPath);
434434
writeStream.on('error', callback);
435435

@@ -440,11 +440,20 @@ Ftp.prototype.get = function(remotePath, localPath, callback) {
440440
socket: this
441441
});
442442
});
443+
444+
// This ensures that any expected outcome is handled. There is no
445+
// danger of the callback being executed several times, because it is
446+
// wrapped in `once`.
447+
socket.on('error', callback);
443448
socket.on('end', callback);
449+
socket.on('close', callback);
450+
444451
socket.pipe(writeStream);
445452
socket.resume();
446-
});
453+
};
447454
}
455+
456+
this.getGetSocket(remotePath, finalCallback);
448457
};
449458

450459
/**
@@ -473,12 +482,19 @@ Ftp.prototype.getGetSocket = function(path, callback) {
473482
socket.pause();
474483

475484
function cmdCallback(err, res) {
476-
if (err) return callback(err);
485+
if (err) {
486+
return callback(err);
487+
}
477488

478-
if (res.code === 125 || res.code === 150)
479-
callback(null, socket);
480-
else
481-
callback(new Error('Unexpected command ' + res.text));
489+
if (!socket) {
490+
return callback(new Error('Error when retrieving PASV socket'));
491+
}
492+
493+
if (res.code === 125 || res.code === 150) {
494+
return callback(null, socket);
495+
}
496+
497+
return callback(new Error('Unexpected command ' + res.text));
482498
}
483499

484500
cmdCallback.expectsMark = {
@@ -547,7 +563,7 @@ Ftp.prototype.getPutSocket = function(path, callback, doneCallback) {
547563
callback(err);
548564
return doneCallback(err);
549565
}
550-
return callback(err, _socket);
566+
return callback(null, _socket);
551567
});
552568

553569
var self = this;
@@ -563,15 +579,17 @@ Ftp.prototype.getPutSocket = function(path, callback, doneCallback) {
563579
socket.on('close', doneCallback);
564580
socket.on('error', doneCallback);
565581
self.pasvTimeout.call(self, socket, doneCallback);
566-
_callback(null, socket);
567-
} else {
568-
return _callback(new Error('Unexpected command ' + res.text));
582+
return _callback(null, socket);
569583
}
584+
585+
return _callback(new Error('Unexpected command ' + res.text));
570586
});
587+
571588
putCallback.expectsMark = {
572589
marks: [125, 150],
573590
ignore: 226
574591
};
592+
575593
self.execute('stor ' + path, putCallback);
576594
});
577595
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "jsftp",
33
"id": "jsftp",
4-
"version": "1.3.8",
4+
"version": "1.3.9",
55
"description": "A sane FTP client implementation for NodeJS",
66
"keywords": [ "ftp", "protocol", "files", "server", "client", "async" ],
77
"author": "Sergi Mansilla <sergi.mansilla@gmail.com> (http://sergimansilla.com)",

0 commit comments

Comments
 (0)