-
Notifications
You must be signed in to change notification settings - Fork 6
/
index.js
134 lines (113 loc) · 4.38 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
const { Readable } = require('stream');
const { format } = require('util');
const { Client } = require('ssh2');
const debug = require('debug')('whoosh:client');
const ssh2Debug = require('debug')('whoosh:ssh2');
const _ = require('lodash');
module.exports = {
connect(config, next) {
const connection = new Client();
const once = _.once(next);
const connectionUrl = format('%s@%s:%s', config.username, config.hostname, config.port);
let disconnected = true;
let disconnecting = false;
debug('Connecting to server %s', connectionUrl);
connection.connect(_.defaults(config, { debug: ssh2Debug }));
connection.on('ready', () => {
debug('Connected to server %s', connectionUrl);
disconnected = false;
connection.sftp((err, sftp) => {
if (err) {
connection.end();
return next(err);
}
sftp = _.extend(sftp, {
getContent(remotePath, options, cb) {
if (arguments.length === 2) return sftp.getContent(remotePath, {}, arguments[1]);
const once = _.once(cb);
let content = '';
debug('Creating read stream to %s/%s', connectionUrl, remotePath);
const readStream = sftp.createReadStream(remotePath, options);
const before = Date.now();
readStream
.on('data', (chunk) => {
content += chunk;
})
.on('end', () => {
const duration = Date.now() - before;
const bytes = countBytes(content);
debug('Downloaded %d bytes from %s/%s in %dms', bytes, connectionUrl, remotePath, duration);
once(null, content, { bytes, duration });
})
.on('error', once);
},
putContent(content, remotePath, options, cb) {
if (arguments.length === 3) return sftp.putContent(content, remotePath, {}, arguments[2]);
const once = _.once(cb);
debug('Creating write stream to %s/%s', connectionUrl, remotePath);
const writeStream = sftp.createWriteStream(remotePath, options);
const before = Date.now();
writeStream
.on('close', () => {
const duration = Date.now() - before;
const bytes = countBytes(content);
debug('Uploaded %d bytes to %s/%s in %sms', bytes, connectionUrl, remotePath, duration);
once(null, { bytes, duration });
})
.on('error', once);
const readStream = new Readable();
readStream.push(content);
readStream.push(null);
readStream.pipe(writeStream);
},
exists(remotePath, cb) {
sftp.stat(remotePath, (err, stat) => {
if (err && err.code !== 2) return cb(err);
return cb(null, !!stat);
});
},
disconnect(cb = _.noop) {
if (!sftp.isConnected()) return cb();
disconnecting = true;
sftp.end();
connection.end();
connection.once('close', cb);
},
isConnected(cb) {
const connected = !disconnected && !disconnecting;
return (cb && cb(null, connected)) || connected;
},
});
once(null, sftp);
});
});
connection.on('keyboard-interactive', (name, instructions, lang, prompts, finish) => {
const responses = _.map(prompts, (entry) => {
const challenge = _.find(config.challenges, (candidate) => candidate.pattern.test(entry.prompt));
if (challenge) return challenge.response;
debug('No response for challenge: %s', entry.prompt);
return '';
});
finish(responses);
});
connection.on('error', (err) => {
debug('Received error from connection: %s:%s. Original error was: ', config.hostname, config.port, err.message);
once(err);
});
connection.on('end', () => {
debug('Connection to %s:%s ended', config.hostname, config.port);
disconnect();
});
connection.on('close', () => {
debug('Connection to %s:%s closed', config.hostname, config.port);
disconnect();
});
function disconnect() {
disconnected = true;
disconnecting = false;
}
},
};
function countBytes(content) {
return Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content);
}