记录一次 BUG 发现经历:使用 ssh2-stream sftp 上传文件出错的问题

记录一次 BUG 发现经历:使用 ssh2-stream sftp 上传文件出错的问题

场景

最近的一个业务需求,开发一款桌面端应用,用来部署企业级的物联网应用,需要使用本机进行目标主机进行命令交互。桌面端应用使用的是 electron 进行跨平台开发,远程命令交互使用的 ssh2 模块。应用中使用了 sftp 进行文件上传,而 sftp 功能是在 ssh2 依赖的 ssh2-stream 里的功能实现。

问题

在与后端开发人员进行正常愉快的联调开发时,突然发现之前已经通畅的业务流程突然被中断了,而中断的地方正好是文件上传的步骤。而更奇怪的是,文件上传有多个地方,第 1、2 个地方都正常执行,而第 3 个地方出现问题。上传失败打印的错误非常的模糊。

runtimes/ssh 201909042012340210000: upload src /Users/cloudcome/Downloads/allpktss.tar.gz +46ms

runtimes/ssh 201909042012340210000: upload dst /root/201909042012337810001.tar.gz +0ms

runtimes/ssh upload error Error: Failure

at SFTPStream._transform (/Users/cloudcome/development/app/node_modules/ssh2-streams/lib/sftp.js:412:27)

at SFTPStream.Transform._read (_stream_transform.js:189:10)

at SFTPStream._read (/Users/cloudcome/development/isyscore/app/node_modules/ssh2-streams/lib/sftp.js:183:15)

at SFTPStream.Transform._write (_stream_transform.js:177:12)

at doWrite (_stream_writable.js:417:12)

at writeOrBuffer (_stream_writable.js:401:5)

at SFTPStream.Writable.write (_stream_writable.js:301:11)

at Channel.ondata (_stream_readable.js:713:22)

at Channel.emit (events.js:200:13)

at addChunk (_stream_readable.js:294:12) {

code: 4,

lang: ''

} +6ms

排查

查看错误上下文

错误的原因只有 Failure 7 个字母。

if (pktType === RESPONSE.STATUS) {

/*

uint32 error/status code

string error message (ISO-10646 UTF-8)

string language tag

*/

var code = readInt(buffer, 4, this, callback);

if (code === false) return;

if (code === STATUS_CODE.OK) {

cb();

} else {

// We borrow OpenSSH behavior here, specifically we make the

// message and language fields optional, despite the

// specification requiring them (even if they are empty). This

// helps to avoid problems with buggy implementations that do

// not fully conform to the SFTP(v3) specification.

var msg;

var lang = '';

if (buffer.length >= 12) {

msg = readString(buffer, 8, 'utf8', this, callback);

if (msg === false) return;

if (buffer._pos + 4 < buffer.length) {

lang = readString(buffer, buffer._pos, 'ascii', this, callback);

if (lang === false) return;

}

}

var err = new Error(msg || STATUS_CODE_STR[code] || 'Unknown status');

err.code = code;

err.lang = lang;

cb(err);

}

}

其他 STATUS_CODE_STR 定义为

var STATUS_CODE_STR = {

0: 'No error',

1: 'End of file',

2: 'No such file or directory',

3: 'Permission denied',

4: 'Failure',

5: 'Bad message',

6: 'No connection',

7: 'Connection lost',

8: 'Operation unsupported'

};

执行的 code 是 4,对应结果就是 Failure,定位上下文并没有其他地方可以明确指定错误的原因。

进入目标主机查看文件 /root/201909042012337810001.tar.gz 是否存在,结果显示的是目录!!

[root@master ~]# ll

总用量 13060

drwxr-xr-x 2 root root 6 9月 4 08:12 201909042012337810001.tar.gz

然后接下来一段时间,是搜索如何将 d 修改成 - 的问题,其他 sftp 的使用方法是:

sftp.fastPut(src, dest, { step, mode }, callback);

mode 参数并不能决定路径的属性,遗憾结束。

搜索引擎

通过谷歌搜各种关键字 sfp remotePath directoryssh2-stream fastPut,其中有一个 issue 跟我遇到的是一样的,结果提问题的人自问自答:

I think I put this under the wrong project. Disregard.

代码历史回顾

在上一个版本的时候,可以明确的是,第 3 步是正常执行的,而目前是失败的,因为从代码历史上能够回顾蛛丝马迹。回顾代码 N 遍,提交代码量不多,并且也没有任何影响的地方。最后还讲当前代码回退到上一个版本,惊奇的是,该错误竟然重现了。

代码历史回顾还拉上同事进行一同审阅,遗憾结束。

错误重现

将错写的代码进行最小化,执行的时候,意外的报错的。

const Client = require('ssh2');

const options = {

host: '...',

port: 22,

username: 'root',

password: '...'

};

const src = '/Users/cloudcome/Downloads/test.tar.gz';

const dest = '/root/test-upload';

const client = new Client();

client.on('ready', function() {

client.sftp(function(err, sftp) {

if (err) {

throw err;

}

console.log('ready');

const step = function() {

console.log(arguments);

};

sftp.fastPut(src, dest, { step }, function(err) {

if (err) {

throw err;

}

console.log('upload success');

});

});

});

client.connect(options);

最后报错了,检查了下,dest 的值竟然是目标主机目录。将 dest 修改为 /root/test-upload/abc.tar.gz,如你所愿,结果是成功的。

错误解决

很明显了,文件上传的时候,目标路径 /root/201909042012337810001.tar.gz 已经是个目录了,所以一直上传失败。跟后端确认逻辑,是因为我们的业务有这样的一个需求,目标路径需要提前判断是否满足条件,然后再进行上传。而后端多做了一步,将该路径新建为目录了。因此导致了多次上传失败。

总结

发现错误一定要心平气和,从最直接的方式开始溯源排查,并且逐级定位,一定能解决问题。

以上是 记录一次 BUG 发现经历:使用 ssh2-stream sftp 上传文件出错的问题 的全部内容, 来源链接: utcz.com/z/264129.html

回到顶部