ssh2-sftp-client 7.2.0 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/README.org +20 -6
- package/package.json +16 -16
- package/src/index.js +61 -37
- package/src/utils.js +13 -13
package/README.md
CHANGED
|
@@ -61,11 +61,11 @@ an SFTP client for node.js, a wrapper around [SSH2](https://github.com/mscdex/ss
|
|
|
61
61
|
|
|
62
62
|
Documentation on the methods and available options in the underlying modules can be found on the [SSH2](https://github.com/mscdex/ssh2) project pages.
|
|
63
63
|
|
|
64
|
-
Current stable release is **v7.2.
|
|
64
|
+
Current stable release is **v7.2.3**.
|
|
65
65
|
|
|
66
|
-
Code has been tested against Node versions 14.
|
|
66
|
+
Code has been tested against Node versions 14.19.1, 16.14.2 and 17.8.0
|
|
67
67
|
|
|
68
|
-
Node versions <
|
|
68
|
+
Node versions < 12.x are not supported. However, node v10.x should still work, although some tests will fail due to changes in file system functions used in test setup and tear down.
|
|
69
69
|
|
|
70
70
|
# Installation<a id="sec-2"></a>
|
|
71
71
|
|
|
@@ -103,6 +103,8 @@ sftp.connect({
|
|
|
103
103
|
|
|
104
104
|
- Improved event handling. A listener for a global error event is now defined to catch errors which occur in-between method calls i.e. connection lost in-between calls to the library methods. A new mechanism has also been added for removal of listeners when no longer required.
|
|
105
105
|
|
|
106
|
+
- uploadDir/downloadDir change in 7.2.3. THe uploadDir() and downloadDir() methods previously used fastPut() and fastGet() to transfer data. Unfortunately, not all SFTP servers support the concurrent processing used by these methods. This meant these methods would fail on some platforms. For now, the fastPut() and fastGet() calls have been replaced with plain put() and get() calls. This will mean uploadDir()/downloadDir() will be slower. However, there is other larger changes in the works which should see a significant speed imp[rovement for these (and other methods). We may also add an option which will allow for selection of fastPut()/fastGet().
|
|
107
|
+
|
|
106
108
|
# Documentation<a id="sec-5"></a>
|
|
107
109
|
|
|
108
110
|
The connection options are the same as those offered by the underlying SSH2 module. For full details, please see [SSH2 client methods](https://github.com/mscdex/ssh2#user-content-client-methods)
|
|
@@ -392,7 +394,7 @@ Returns the attributes associated with the object pointed to by `path`.
|
|
|
392
394
|
|
|
393
395
|
Retrieve a file from a remote SFTP server. The `dst` argument defines the destination and can be either a string, a stream object or undefined. If it is a string, it is interpreted as the path to a location on the local file system (path should include the file name). If it is a stream object, the remote data is passed to it via a call to pipe(). If `dst` is undefined, the method will put the data into a buffer and return that buffer when the Promise is resolved. If `dst` is defined, it is returned when the Promise is resolved.
|
|
394
396
|
|
|
395
|
-
In general, if
|
|
397
|
+
In general, if you're going to pass in a string as the destination, you are better off using the `fastGet()` method.
|
|
396
398
|
|
|
397
399
|
- **path:** String. Path to the remote file to download
|
|
398
400
|
- **dst:** String|Stream. Destination for the data. If a string, it should be a local file path.
|
|
@@ -1332,7 +1334,7 @@ Perhaps the best assistance is a minimal reproducible example of the issue. Once
|
|
|
1332
1334
|
|
|
1333
1335
|
# Pull Requests<a id="sec-11"></a>
|
|
1334
1336
|
|
|
1335
|
-
Pull requests are always welcomed. However, please ensure your changes pass all tests and if
|
|
1337
|
+
Pull requests are always welcomed. However, please ensure your changes pass all tests and if you're adding a new feature, that tests for that feature are included. Likewise, for new features or enhancements, please include any relevant documentation updates.
|
|
1336
1338
|
|
|
1337
1339
|
**Note**: The `README.md` file is generated from the `README.org` file. Therefore, any documentation updates or fixes need to be made to the `README.org` file. This file is *tangled* using `Emacs` org mode. If you don't use Emacs or org-mode, don't be too concerned. The org-mode syntax is straight-forward and similar to *markdown*. I will verify any updates to `README.org` and generate a new `README.md` when necessary. The main point to note is that any changes made directly to `README.md` will not persist and will be lost when a new version is generated, so don't modify that file.
|
|
1338
1340
|
|
|
@@ -1361,3 +1363,4 @@ Thanks to the following for their contributions -
|
|
|
1361
1363
|
- **Emma Milner:** Contributed fix for put() bug
|
|
1362
1364
|
- **Witni Davis:** Contributed PR to fix put() RCE when using 'finish' rather than 'close' to resolve promise
|
|
1363
1365
|
- **Maik Marschner:** Contributed fix for connect() not returning sftp object. Also included test to check for this regression in future.
|
|
1366
|
+
- **cakemasher:** Contributed fix for removeTempListeners().
|
package/README.org
CHANGED
|
@@ -9,11 +9,13 @@ convenience abstraction as well as a Promise based API.
|
|
|
9
9
|
Documentation on the methods and available options in the underlying modules can
|
|
10
10
|
be found on the [[https://github.com/mscdex/ssh2][SSH2]] project pages.
|
|
11
11
|
|
|
12
|
-
Current stable release is *v7.2.
|
|
12
|
+
Current stable release is *v7.2.3*.
|
|
13
13
|
|
|
14
|
-
Code has been tested against Node versions 14.
|
|
14
|
+
Code has been tested against Node versions 14.19.1, 16.14.2 and 17.8.0
|
|
15
15
|
|
|
16
|
-
Node versions <
|
|
16
|
+
Node versions < 12.x are not supported. However, node v10.x should still work,
|
|
17
|
+
although some tests will fail due to changes in file system functions used in
|
|
18
|
+
test setup and tear down.
|
|
17
19
|
|
|
18
20
|
* Installation
|
|
19
21
|
|
|
@@ -75,6 +77,16 @@ npm install ssh2-sftp-client
|
|
|
75
77
|
in-between calls to the library methods. A new mechanism has also been added
|
|
76
78
|
for removal of listeners when no longer required.
|
|
77
79
|
|
|
80
|
+
- uploadDir/downloadDir change in 7.2.3. THe uploadDir() and downloadDir()
|
|
81
|
+
methods previously used fastPut() and fastGet() to transfer data.
|
|
82
|
+
Unfortunately, not all SFTP servers support the concurrent processing used by
|
|
83
|
+
these methods. This meant these methods would fail on some platforms. For now,
|
|
84
|
+
the fastPut() and fastGet() calls have been replaced with plain put() and
|
|
85
|
+
get() calls. This will mean uploadDir()/downloadDir() will be slower. However,
|
|
86
|
+
there is other larger changes in the works which should see a significant
|
|
87
|
+
speed imp[rovement for these (and other methods). We may also add an option
|
|
88
|
+
which will allow for selection of fastPut()/fastGet().
|
|
89
|
+
|
|
78
90
|
* Documentation
|
|
79
91
|
|
|
80
92
|
The connection options are the same as those offered by the underlying SSH2
|
|
@@ -422,7 +434,7 @@ is passed to it via a call to pipe(). If ~dst~ is undefined, the method will put
|
|
|
422
434
|
the data into a buffer and return that buffer when the Promise is resolved. If
|
|
423
435
|
~dst~ is defined, it is returned when the Promise is resolved.
|
|
424
436
|
|
|
425
|
-
In general, if
|
|
437
|
+
In general, if you're going to pass in a string as the destination, you are
|
|
426
438
|
better off using the ~fastGet()~ method.
|
|
427
439
|
|
|
428
440
|
- path :: String. Path to the remote file to download
|
|
@@ -1732,10 +1744,10 @@ the issue. Things which will help
|
|
|
1732
1744
|
Perhaps the best assistance is a minimal reproducible example of the issue. Once
|
|
1733
1745
|
the issue can be readily reproduced, it can usually be fixed very quickly.
|
|
1734
1746
|
|
|
1735
|
-
* Pull Requests
|
|
1747
|
+
* Pull Requests
|
|
1736
1748
|
|
|
1737
1749
|
Pull requests are always welcomed. However, please ensure your changes pass all
|
|
1738
|
-
tests and if
|
|
1750
|
+
tests and if you're adding a new feature, that tests for that feature are
|
|
1739
1751
|
included. Likewise, for new features or enhancements, please include any
|
|
1740
1752
|
relevant documentation updates.
|
|
1741
1753
|
|
|
@@ -1784,3 +1796,5 @@ Thanks to the following for their contributions -
|
|
|
1784
1796
|
'close' to resolve promise
|
|
1785
1797
|
- Maik Marschner :: Contributed fix for connect() not returning sftp object.
|
|
1786
1798
|
Also included test to check for this regression in future.
|
|
1799
|
+
- cakemasher :: Contributed fix for removeTempListeners().
|
|
1800
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssh2-sftp-client",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.3",
|
|
4
4
|
"description": "ssh2 sftp client for node",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -25,28 +25,28 @@
|
|
|
25
25
|
}
|
|
26
26
|
],
|
|
27
27
|
"license": "Apache-2.0",
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"concat-stream": "^2.0.0",
|
|
30
|
-
"promise-retry": "^2.0.1",
|
|
31
|
-
"ssh2": "^1.5.0"
|
|
32
|
-
},
|
|
33
28
|
"devDependencies": {
|
|
34
|
-
"chai": "^4.3.
|
|
29
|
+
"chai": "^4.3.6",
|
|
35
30
|
"chai-as-promised": "^7.1.1",
|
|
36
31
|
"chai-subset": "^1.6.0",
|
|
37
32
|
"checksum": "^1.0.0",
|
|
38
|
-
"dotenv": "^
|
|
39
|
-
"eslint": "^8.
|
|
40
|
-
"eslint-config-prettier": "^8.
|
|
41
|
-
"eslint-plugin-mocha": "^
|
|
33
|
+
"dotenv": "^16.0.0",
|
|
34
|
+
"eslint": "^8.12.0",
|
|
35
|
+
"eslint-config-prettier": "^8.5.0",
|
|
36
|
+
"eslint-plugin-mocha": "^10.0.3",
|
|
42
37
|
"eslint-plugin-node": "^11.1.0",
|
|
43
|
-
"eslint-plugin-promise": "^
|
|
44
|
-
"eslint-plugin-unicorn": "^
|
|
45
|
-
"mocha": "^9.
|
|
38
|
+
"eslint-plugin-promise": "^6.0.0",
|
|
39
|
+
"eslint-plugin-unicorn": "^41.0.1",
|
|
40
|
+
"mocha": "^9.2.2",
|
|
46
41
|
"moment": "^2.29.1",
|
|
47
42
|
"nyc": "^15.1.0",
|
|
48
|
-
"prettier": "^2.
|
|
43
|
+
"prettier": "^2.6.1",
|
|
49
44
|
"through2": "^4.0.2",
|
|
50
|
-
"winston": "^3.
|
|
45
|
+
"winston": "^3.6.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"concat-stream": "^2.0.0",
|
|
49
|
+
"promise-retry": "^2.0.1",
|
|
50
|
+
"ssh2": "^1.8.0"
|
|
51
51
|
}
|
|
52
52
|
}
|
package/src/index.js
CHANGED
|
@@ -59,7 +59,7 @@ class SftpClient {
|
|
|
59
59
|
this.client.on('error', (err) => {
|
|
60
60
|
if (this.endCalled || this.errorHandled) {
|
|
61
61
|
// error event expected or handled elsewhere
|
|
62
|
-
this.debugMsg(
|
|
62
|
+
this.debugMsg(`Global: Ignoring handled error: ${err.message}`);
|
|
63
63
|
} else {
|
|
64
64
|
this.debugMsg(`Global; Handling unexpected error; ${err.message}`);
|
|
65
65
|
this.sftp = undefined;
|
|
@@ -120,28 +120,24 @@ class SftpClient {
|
|
|
120
120
|
*/
|
|
121
121
|
getConnection(config) {
|
|
122
122
|
let doReady;
|
|
123
|
-
return (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
removeTempListeners(this, 'getConnection');
|
|
142
|
-
this._resetEventFlags();
|
|
143
|
-
})
|
|
144
|
-
);
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
addTempListeners(this, 'getConnection', reject);
|
|
125
|
+
this.debugMsg('getConnection: created promise');
|
|
126
|
+
doReady = () => {
|
|
127
|
+
this.debugMsg(
|
|
128
|
+
'getConnection ready listener: got connection - promise resolved'
|
|
129
|
+
);
|
|
130
|
+
resolve(true);
|
|
131
|
+
};
|
|
132
|
+
this.on('ready', doReady);
|
|
133
|
+
this.client.connect(config);
|
|
134
|
+
}).finally(async () => {
|
|
135
|
+
this.debugMsg('getConnection: finally clause fired');
|
|
136
|
+
await sleep(500);
|
|
137
|
+
this.removeListener('ready', doReady);
|
|
138
|
+
removeTempListeners(this, 'getConnection');
|
|
139
|
+
this._resetEventFlags();
|
|
140
|
+
});
|
|
145
141
|
}
|
|
146
142
|
|
|
147
143
|
getSftpChannel() {
|
|
@@ -196,8 +192,17 @@ class SftpClient {
|
|
|
196
192
|
(retry, attempt) => {
|
|
197
193
|
this.debugMsg(`connect: Connect attempt ${attempt}`);
|
|
198
194
|
return this.getConnection(config).catch((err) => {
|
|
199
|
-
this.debugMsg(
|
|
200
|
-
|
|
195
|
+
this.debugMsg(
|
|
196
|
+
`getConnection retry catch: ${err.message} Code: ${err.code}`
|
|
197
|
+
);
|
|
198
|
+
switch (err.code) {
|
|
199
|
+
case 'ENOTFOUND':
|
|
200
|
+
case 'ECONNREFUSED':
|
|
201
|
+
case 'ERR_SOCKET_BAD_PORT':
|
|
202
|
+
throw err;
|
|
203
|
+
default:
|
|
204
|
+
retry(err);
|
|
205
|
+
}
|
|
201
206
|
});
|
|
202
207
|
},
|
|
203
208
|
{
|
|
@@ -515,13 +520,29 @@ class SftpClient {
|
|
|
515
520
|
)
|
|
516
521
|
);
|
|
517
522
|
});
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
523
|
+
if (
|
|
524
|
+
Object.hasOwnProperty.call(options, 'pipeOptions') &&
|
|
525
|
+
Object.hasOwnProperty.call(options.pipeOptions, 'end') &&
|
|
526
|
+
!options.pipeOptions.end
|
|
527
|
+
) {
|
|
528
|
+
rdr.once('end', () => {
|
|
529
|
+
this.debugMsg('get resolved on reader end event');
|
|
530
|
+
if (typeof dst === 'string') {
|
|
531
|
+
resolve(dst);
|
|
532
|
+
} else {
|
|
533
|
+
resolve(wtr);
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
} else {
|
|
537
|
+
wtr.once('finish', () => {
|
|
538
|
+
this.debugMsg('get resolved on writer finish event');
|
|
539
|
+
if (typeof dst === 'string') {
|
|
540
|
+
resolve(dst);
|
|
541
|
+
} else {
|
|
542
|
+
resolve(wtr);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
}
|
|
525
546
|
}
|
|
526
547
|
rdr.pipe(wtr, options.pipeOptions ? options.pipeOptions : {});
|
|
527
548
|
}
|
|
@@ -530,14 +551,16 @@ class SftpClient {
|
|
|
530
551
|
this._resetEventFlags();
|
|
531
552
|
if (
|
|
532
553
|
rdr &&
|
|
533
|
-
options
|
|
554
|
+
Object.hasOwnProperty.call(options, 'readStreamOptions') &&
|
|
555
|
+
Object.hasOwnProperty.call(options.readStreamOptions, 'autoClose') &&
|
|
534
556
|
options.readStreamOptions.autoClose === false
|
|
535
557
|
) {
|
|
536
558
|
rdr.destroy();
|
|
537
559
|
}
|
|
538
560
|
if (
|
|
539
561
|
wtr &&
|
|
540
|
-
options
|
|
562
|
+
Object.hasOwnProperty.call(options, 'writeStreamOptions') &&
|
|
563
|
+
Object.hasOwnProperty.call(options.writeStreamOptions, 'autoClose') &&
|
|
541
564
|
options.writeStreamOptions.autoClose === false &&
|
|
542
565
|
typeof dst === 'string'
|
|
543
566
|
) {
|
|
@@ -747,7 +770,8 @@ class SftpClient {
|
|
|
747
770
|
this._resetEventFlags();
|
|
748
771
|
if (
|
|
749
772
|
rdr &&
|
|
750
|
-
options
|
|
773
|
+
Object.hasOwnProperty.call(options, 'readStreamOptions') &&
|
|
774
|
+
Object.hasOwnProperty.call(options.readStreamOptions, 'autoClose') &&
|
|
751
775
|
options.readStreamOptions.autoClose === false &&
|
|
752
776
|
typeof localSrc === 'string'
|
|
753
777
|
) {
|
|
@@ -1110,7 +1134,7 @@ class SftpClient {
|
|
|
1110
1134
|
} else if (e.isFile()) {
|
|
1111
1135
|
let src = join(srcDir, e.name);
|
|
1112
1136
|
let dst = dstDir + this.remotePathSep + e.name;
|
|
1113
|
-
await this.
|
|
1137
|
+
await this.put(src, dst);
|
|
1114
1138
|
this.client.emit('upload', { source: src, destination: dst });
|
|
1115
1139
|
} else {
|
|
1116
1140
|
this.debugMsg(
|
|
@@ -1166,7 +1190,7 @@ class SftpClient {
|
|
|
1166
1190
|
} else if (f.type === '-') {
|
|
1167
1191
|
let src = srcDir + this.remotePathSep + f.name;
|
|
1168
1192
|
let dst = join(dstDir, f.name);
|
|
1169
|
-
await this.
|
|
1193
|
+
await this.get(src, dst);
|
|
1170
1194
|
this.client.emit('download', { source: src, destination: dst });
|
|
1171
1195
|
} else {
|
|
1172
1196
|
this.debugMsg(
|
package/src/utils.js
CHANGED
|
@@ -76,15 +76,15 @@ function addToTempListenerList(obj, name, evt, fn) {
|
|
|
76
76
|
function errorListener(client, name, reject) {
|
|
77
77
|
let fn = (err) => {
|
|
78
78
|
if (client.endCalled || client.errorHandled) {
|
|
79
|
-
client.debugMsg(`${name}: Ignoring handled error: ${err.message}`);
|
|
79
|
+
client.debugMsg(`${name} Error: Ignoring handled error: ${err.message}`);
|
|
80
80
|
} else {
|
|
81
|
-
client.debugMsg(`${name}: Handling error: ${err.message}`);
|
|
81
|
+
client.debugMsg(`${name} Error: Handling error: ${err.message}`);
|
|
82
82
|
client.errorHandled = true;
|
|
83
83
|
if (reject) {
|
|
84
|
-
client.debugMsg(`${name}: handled error with reject`);
|
|
84
|
+
client.debugMsg(`${name} Error: handled error with reject`);
|
|
85
85
|
reject(fmtError(err, name, err.code));
|
|
86
86
|
} else {
|
|
87
|
-
client.debugMsg(`${name}: handling error with throw`);
|
|
87
|
+
client.debugMsg(`${name} Error: handling error with throw`);
|
|
88
88
|
throw fmtError(err, name, err.code);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -96,16 +96,16 @@ function errorListener(client, name, reject) {
|
|
|
96
96
|
function endListener(client, name, reject) {
|
|
97
97
|
let fn = function () {
|
|
98
98
|
if (client.endCalled || client.endHandled) {
|
|
99
|
-
client.debugMsg(`${name}: Ignoring expected end event`);
|
|
99
|
+
client.debugMsg(`${name} End: Ignoring expected end event`);
|
|
100
100
|
} else {
|
|
101
|
-
client.debugMsg(`${name}: Handling end event`);
|
|
101
|
+
client.debugMsg(`${name} End: Handling end event`);
|
|
102
102
|
client.sftp = undefined;
|
|
103
103
|
client.endHandled = true;
|
|
104
104
|
if (reject) {
|
|
105
|
-
client.debugMsg(`${name}: handling end event with reject'`);
|
|
105
|
+
client.debugMsg(`${name} End: handling end event with reject'`);
|
|
106
106
|
reject(fmtError('Unexpected end event raised', name));
|
|
107
107
|
} else {
|
|
108
|
-
client.debugMsg(`${name}: handling end event with throw`);
|
|
108
|
+
client.debugMsg(`${name} End: handling end event with throw`);
|
|
109
109
|
throw fmtError('Unexpected end event raised', name);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -117,16 +117,16 @@ function endListener(client, name, reject) {
|
|
|
117
117
|
function closeListener(client, name, reject) {
|
|
118
118
|
let fn = function () {
|
|
119
119
|
if (client.endCalled || client.closeHandled) {
|
|
120
|
-
client.debugMsg(`${name}: ignoring expected close event`);
|
|
120
|
+
client.debugMsg(`${name} Close: ignoring expected close event`);
|
|
121
121
|
} else {
|
|
122
|
-
client.debugMsg(`${name}: handling unexpected close event`);
|
|
122
|
+
client.debugMsg(`${name} Close: handling unexpected close event`);
|
|
123
123
|
client.sftp = undefined;
|
|
124
124
|
client.closeHandled = true;
|
|
125
125
|
if (reject) {
|
|
126
|
-
client.debugMsg(`${name}: handling close event with reject`);
|
|
126
|
+
client.debugMsg(`${name} Close: handling close event with reject`);
|
|
127
127
|
reject(fmtError('Unexpected close event raised', name));
|
|
128
128
|
} else {
|
|
129
|
-
client.debugMsg(`${name}: handling close event with throw`);
|
|
129
|
+
client.debugMsg(`${name} Close: handling close event with throw`);
|
|
130
130
|
throw fmtError('Unexpected close event raised', name);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
@@ -148,7 +148,7 @@ function removeTempListeners(obj, name) {
|
|
|
148
148
|
obj.tempListeners[name].forEach(([e, fn]) => {
|
|
149
149
|
obj.client.removeListener(e, fn);
|
|
150
150
|
});
|
|
151
|
-
obj.tempListeners = [];
|
|
151
|
+
obj.tempListeners[name] = [];
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|