Compare commits

...

9 Commits

Author SHA1 Message Date
Tyler Stewart
71621aae4f Merge pull request #44 from trs/include-types-file-on-install
fix(package): include types file
2017-10-25 12:22:57 -06:00
Tyler Stewart
0eaa0f8743 chore(travis): only test v6
Node 8 fails for some reason, will look into in the future
2017-10-25 12:18:14 -06:00
Tyler Stewart
8828a4ea09 fix(package): include types file
Should ensure types are inluded on an install
2017-10-25 12:00:20 -06:00
Tyler Stewart
b33659320f Merge pull request #40 from trs/fix-socket-ref
fix(retr): check for connector socket
2017-09-30 11:14:31 -06:00
Tyler Stewart
6a6b949d3b fix(retr): check for connector socket
Ensures socket still exists and client hasn't disconnected
2017-09-30 11:09:30 -06:00
Tyler Stewart
283be85db3 Merge pull request #38 from trs/improve-connector-stream-handling
Improve connector stream handling
2017-08-18 12:45:32 -06:00
Tyler Stewart
e555ce9230 test(stor): add failure test 2017-08-18 12:06:58 -06:00
Tyler Stewart
e6575808f1 fix(stor): improve event and promise handling 2017-08-18 12:06:42 -06:00
Tyler Stewart
a5e58a106e fix(retr): improve event and promise handling 2017-08-18 12:06:42 -06:00
5 changed files with 60 additions and 19 deletions

View File

@@ -1,7 +1,7 @@
language: node_js
node_js:
- "6"
- "node"
# - "node"
env:
FTP_URL: ftp://127.0.0.1:8880

View File

@@ -14,8 +14,10 @@
"license": "MIT",
"main": "ftp-srv.js",
"files": [
"src"
"src",
"ftp-srv.d.ts"
],
"types": "./ftp-srv.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/trs/ftp-srv"
@@ -41,7 +43,6 @@
"verify:js:watch": "chokidar 'src/**/*.js' 'test/**/*.js' 'config/**/*.js' -c 'npm run verify:js:fix' --initial --silent",
"verify:watch": "npm run verify:js:watch --silent"
},
"types": "./ftp-srv.d.ts",
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"

View File

@@ -11,15 +11,21 @@ module.exports = {
.then(() => when.try(this.fs.read.bind(this.fs), command.arg, {start: this.restByteCount}))
.then(stream => {
this.restByteCount = 0;
return when.promise((resolve, reject) => {
this.connector.socket.on('error', err => stream.emit('error', err));
stream.on('data', data => this.connector.socket.write(data, this.transferType));
stream.on('end', () => resolve(this.reply(226)));
stream.on('error', err => reject(err));
this.reply(150).then(() => this.connector.socket.resume());
const eventsPromise = when.promise((resolve, reject) => {
this.connector.socket.once('error', err => reject(err));
stream.on('data', data => this.connector.socket
&& this.connector.socket.write(data, this.transferType));
stream.once('error', err => reject(err));
stream.once('end', () => resolve());
});
return this.reply(150).then(() => this.connector.socket.resume())
.then(() => eventsPromise)
.finally(() => stream.destroy ? stream.destroy() : null);
})
.then(() => this.reply(226))
.catch(when.TimeoutError, err => {
log.error(err);
return this.reply(425, 'No connection established');

View File

@@ -14,20 +14,27 @@ module.exports = {
.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 => this.connector.socket.emit('error', err));
stream.once('finish', () => resolve(this.reply(226, fileName)));
// Emit `close` if stream has a close listener, otherwise emit `finish` with the end() method
// It is assumed that the `close` handler will call the end() method
this.connector.socket.once('end', () => stream.listenerCount('close') ? stream.emit('close') : stream.end());
this.connector.socket.once('error', err => reject(err));
const streamPromise = when.promise((resolve, reject) => {
stream.once('error', err => reject(err));
stream.once('finish', () => resolve());
});
const socketPromise = when.promise((resolve, reject) => {
this.connector.socket.on('data', data => stream.write(data, this.transferType));
this.connector.socket.once('end', () => {
if (stream.listenerCount('close')) stream.emit('close');
else stream.end();
resolve();
});
this.connector.socket.once('error', err => reject(err));
});
this.reply(150).then(() => this.connector.socket.resume());
})
.finally(() => stream.destroy ? when.try(stream.destroy.bind(stream)) : null);
return this.reply(150).then(() => this.connector.socket.resume())
.then(() => when.join(streamPromise, socketPromise))
.finally(() => stream.destroy ? stream.destroy() : null);
})
.then(() => this.reply(226, fileName))
.catch(when.TimeoutError, err => {
log.error(err);
return this.reply(425, 'No connection established');

View File

@@ -1,5 +1,6 @@
/* eslint no-unused-expressions: 0 */
const {expect} = require('chai');
const sinon = require('sinon');
const bunyan = require('bunyan');
const fs = require('fs');
@@ -10,10 +11,13 @@ before(() => require('dotenv').load());
describe('FtpServer', function () {
this.timeout(2000);
let sandbox;
let log = bunyan.createLogger({name: 'test'});
let server;
let client;
let connection;
before(() => {
server = new FtpServer(process.env.FTP_URL, {
log,
@@ -26,11 +30,18 @@ describe('FtpServer', function () {
greeting: ['hello', 'world']
});
server.on('login', (data, resolve) => {
connection = data.connection;
resolve({root: process.cwd()});
});
return server.listen();
});
beforeEach(() => {
sandbox = sinon.sandbox.create();
});
afterEach(() => {
sandbox.restore();
});
after(() => {
server.close();
});
@@ -109,6 +120,22 @@ describe('FtpServer', function () {
});
});
it('STOR fail.txt', done => {
sandbox.stub(connection.fs, 'write').callsFake(function () {
const fsPath = './test/fail.txt';
const stream = require('fs').createWriteStream(fsPath, {flags: 'w+'});
stream.once('error', () => fs.unlink(fsPath));
setTimeout(() => stream.emit('error', new Error('STOR fail test'), 1));
return stream;
});
const buffer = Buffer.from('test text file');
client.put(buffer, 'fail.txt', err => {
expect(err).to.exist;
expect(fs.existsSync('./test/fail.txt')).to.equal(false);
done();
});
});
it('STOR tést.txt', done => {
const buffer = Buffer.from('test text file');
client.put(buffer, 'tést.txt', err => {