Skip to content

Commit

Permalink
feat(REST): add support for REST command
Browse files Browse the repository at this point in the history
Allows the client to resume a transfer at the specified bytes
  • Loading branch information
trs committed Jun 26, 2017
1 parent 8aeb697 commit 2e02dc2
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 9 deletions.
16 changes: 16 additions & 0 deletions src/commands/registration/rest.js
@@ -0,0 +1,16 @@
const _ = require('lodash');

module.exports = {
directive: 'REST',
handler: function ({command} = {}) {
const arg = _.get(command, 'arg');
const byteCount = parseInt(arg, 10);

if (isNaN(byteCount) || byteCount < 0) return this.reply(501, 'Byte count must be 0 or greater');

this.restByteCount = byteCount;
return this.reply(350, `Resarting next transfer at ${byteCount}`);
},
syntax: '{{cmd}} <byte-count>',
description: 'Restart transfer from the specified point. Resets after any STORE or RETRIEVE'
};
3 changes: 2 additions & 1 deletion src/commands/registration/retr.js
Expand Up @@ -12,8 +12,9 @@ module.exports = {
this.commandSocket.pause();
dataSocket = socket;
})
.then(() => when.try(this.fs.read.bind(this.fs), command.arg))
.then(() => when.try(this.fs.read.bind(this.fs), command.arg, {start: this.restByteCount}))
.then(stream => {
this.restByteCount = 0;
return when.promise((resolve, reject) => {
dataSocket.on('error', err => stream.emit('error', err));

Expand Down
3 changes: 2 additions & 1 deletion src/commands/registration/stor.js
Expand Up @@ -15,8 +15,9 @@ module.exports = {
this.commandSocket.pause();
dataSocket = socket;
})
.then(() => when.try(this.fs.write.bind(this.fs), fileName, {append}))
.then(() => when.try(this.fs.write.bind(this.fs), fileName, {append, start: this.restByteCount}))
.then(stream => {
this.restByteCount = 0;
return when.promise((resolve, reject) => {
stream.once('error', err => dataSocket.emit('error', err));
stream.once('finish', () => resolve(this.reply(226, fileName)));
Expand Down
1 change: 0 additions & 1 deletion src/commands/registration/type.js
Expand Up @@ -2,7 +2,6 @@
module.exports = {
directive: 'TYPE',
handler: function ({command} = {}) {

if (/^A[0-9]?$/i.test(command.arg)) {
this.transferType = 'ascii';
} else if (/^L[0-9]?$/i.test(command.arg) || /^I$/i.test(command.arg)) {
Expand Down
1 change: 1 addition & 0 deletions src/commands/registry.js
Expand Up @@ -21,6 +21,7 @@ const commands = [
require('./registration/port'),
require('./registration/pwd'),
require('./registration/quit'),
require('./registration/rest'),
require('./registration/retr'),
require('./registration/rmd'),
require('./registration/rnfr'),
Expand Down
11 changes: 9 additions & 2 deletions src/connection.js
Expand Up @@ -18,7 +18,7 @@ class FtpConnection {
this.transferType = 'binary';
this.encoding = 'utf8';
this.bufferSize = false;
this.restByteCount = 0;
this._restByteCount = 0;
this._secure = false;

this.connector = new BaseConnector(this);
Expand Down Expand Up @@ -50,6 +50,13 @@ class FtpConnection {
}
}

get restByteCount() {
return this._restByteCount > 0 ? this._restByteCount : undefined;
}
set restByteCount(rbc) {
this._restByteCount = rbc;
}

get secure() {
return this.server.isTLS || this._secure;
}
Expand All @@ -68,7 +75,7 @@ class FtpConnection {
return when.try(() => {
const loginListeners = this.server.listeners('login');
if (!loginListeners || !loginListeners.length) {
if (!this.server.options.anoymous) throw new errors.GeneralError('No "login" listener setup', 500);
if (!this.server.options.anonymous) throw new errors.GeneralError('No "login" listener setup', 500);
} else {
return this.server.emitPromise('login', {connection: this, username, password});
}
Expand Down
8 changes: 4 additions & 4 deletions src/fs.js
Expand Up @@ -65,22 +65,22 @@ class FileSystem {
});
}

write(fileName, {append = false} = {}) {
write(fileName, {append = false, start = undefined} = {}) {
const {fsPath} = this._resolvePath(fileName);
const stream = syncFs.createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+'});
const stream = syncFs.createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+', start});
stream.once('error', () => fs.unlink(fsPath));
stream.once('close', () => stream.end());
return stream;
}

read(fileName) {
read(fileName, {start = undefined} = {}) {
const {fsPath} = this._resolvePath(fileName);
return fs.stat(fsPath)
.tap(stat => {
if (stat.isDirectory()) throw new errors.FileSystemError('Cannot read a directory');
})
.then(() => {
const stream = syncFs.createReadStream(fsPath, {flags: 'r'});
const stream = syncFs.createReadStream(fsPath, {flags: 'r', start});
return stream;
});
}
Expand Down

0 comments on commit 2e02dc2

Please sign in to comment.