Files
bare-operating-system/packages/bare-os-openssh/vendor/bare-ssh2/test/test-sftp.js
T

1249 lines
32 KiB
JavaScript

'use strict'
const assert = require('./assert-compat.js')
const { constants } = require('fs')
const {
fixture,
mustCall,
mustCallAtLeast,
mustNotCall,
setup: setup_,
setupSimple
} = require('./common.js')
const { OPEN_MODE, Stats, STATUS_CODE } = require('../lib/protocol/SFTP.js')
const DEBUG = false
setup(
'open',
mustCall((client, server) => {
const path_ = '/tmp/foo.txt'
const handle_ = Buffer.from('node.js')
const pflags_ = OPEN_MODE.TRUNC | OPEN_MODE.CREAT | OPEN_MODE.WRITE
server.on(
'OPEN',
mustCall((id, path, pflags, attrs) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
assert(pflags === pflags_, `Wrong flags: ${flagsToHuman(pflags)}`)
server.handle(id, handle_)
server.end()
})
)
client.open(
path_,
'w',
mustCall((err, handle) => {
assert(!err, `Unexpected open() error: ${err}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
})
)
})
)
setup(
'close',
mustCall((client, server) => {
const handle_ = Buffer.from('node.js')
server.on(
'CLOSE',
mustCall((id, handle) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.close(
handle_,
mustCall((err) => {
assert(!err, `Unexpected close() error: ${err}`)
})
)
})
)
setup(
'read',
mustCall((client, server) => {
const expected = Buffer.from('node.jsnode.jsnode.jsnode.jsnode.jsnode.js')
const handle_ = Buffer.from('node.js')
const buf = Buffer.alloc(expected.length)
server.on(
'READ',
mustCall((id, handle, offset, len) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert(offset === 5, `Wrong read offset: ${offset}`)
assert(len === buf.length, `Wrong read len: ${len}`)
server.data(id, expected)
server.end()
})
)
client.read(
handle_,
buf,
0,
buf.length,
5,
mustCall((err, nb) => {
assert(!err, `Unexpected read() error: ${err}`)
assert.deepStrictEqual(buf, expected, 'read data mismatch')
})
)
})
)
setup(
'read (partial)',
mustCall((client, server) => {
const expected = Buffer.from('blargh')
const handle_ = Buffer.from('node.js')
const buf = Buffer.alloc(256)
server.on(
'READ',
mustCall((id, handle, offset, len) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert(offset === 0, `Wrong read offset: ${offset}`)
assert(len === buf.length, `Wrong read len: ${len}`)
server.data(id, expected)
server.end()
})
)
client.read(
handle_,
buf,
0,
buf.length,
0,
mustCall((err, nb, data) => {
assert(!err, `Unexpected read() error: ${err}`)
assert.strictEqual(nb, expected.length, 'nb count mismatch')
assert.deepStrictEqual(buf.slice(0, expected.length), expected, 'read data mismatch')
assert.deepStrictEqual(data, expected, 'read data mismatch')
})
)
})
)
setup(
'read (overflow)',
mustCall((client, server) => {
const maxChunk = client._maxReadLen
const expected = Buffer.alloc(3 * maxChunk, 'Q')
const handle_ = Buffer.from('node.js')
const buf = Buffer.alloc(expected.length, 0)
let reqs = 0
server.on(
'READ',
mustCall((id, handle, offset, len) => {
++reqs
assert.strictEqual(id, reqs - 1, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert.strictEqual(offset, (reqs - 1) * maxChunk, `Wrong read offset: ${offset}`)
server.data(id, expected.slice(offset, offset + len))
if (reqs === 3) server.end()
}, 3)
)
client.read(
handle_,
buf,
0,
buf.length,
0,
mustCall((err, nb) => {
assert(!err, `Unexpected read() error: ${err}`)
assert.deepStrictEqual(buf, expected)
assert.strictEqual(nb, buf.length, 'read nb mismatch')
})
)
})
)
setup(
'write',
mustCall((client, server) => {
const handle_ = Buffer.from('node.js')
const buf = Buffer.from('node.jsnode.jsnode.jsnode.jsnode.jsnode.js')
server.on(
'WRITE',
mustCall((id, handle, offset, data) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert(offset === 5, `Wrong write offset: ${offset}`)
assert.deepStrictEqual(data, buf, 'write data mismatch')
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.write(
handle_,
buf,
0,
buf.length,
5,
mustCall((err, nb) => {
assert(!err, `Unexpected write() error: ${err}`)
assert.strictEqual(nb, buf.length, 'wrong bytes written')
})
)
})
)
setup(
'write (overflow)',
mustCall((client, server) => {
const maxChunk = client._maxWriteLen
const handle_ = Buffer.from('node.js')
const buf = Buffer.allocUnsafe(3 * maxChunk)
let reqs = 0
server.on(
'WRITE',
mustCall((id, handle, offset, data) => {
++reqs
assert.strictEqual(id, reqs - 1, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert.strictEqual(offset, (reqs - 1) * maxChunk, `Wrong write offset: ${offset}`)
assert(offset + data.length <= buf.length, 'bad offset')
assert.deepStrictEqual(data, buf.slice(offset, offset + data.length), 'write data mismatch')
server.status(id, STATUS_CODE.OK)
if (reqs === 3) server.end()
}, 3)
)
client.write(
handle_,
buf,
0,
buf.length,
0,
mustCall((err, nb) => {
assert(!err, `Unexpected write() error: ${err}`)
assert.strictEqual(nb, buf.length, 'wrote bytes written')
})
)
})
)
setup(
'lstat',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
const attrs_ = new Stats({
size: 10 * 1024,
uid: 9001,
gid: 9001,
atime: (Date.now() / 1000) | 0,
mtime: (Date.now() / 1000) | 0
})
server.on(
'LSTAT',
mustCall((id, path) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
server.attrs(id, attrs_)
server.end()
})
)
client.lstat(
path_,
mustCall((err, attrs) => {
assert(!err, `Unexpected lstat() error: ${err}`)
assert.deepStrictEqual(attrs, attrs_, 'attrs mismatch')
})
)
})
)
setup(
'fstat',
mustCall((client, server) => {
const handle_ = Buffer.from('node.js')
const attrs_ = new Stats({
size: 10 * 1024,
uid: 9001,
gid: 9001,
atime: (Date.now() / 1000) | 0,
mtime: (Date.now() / 1000) | 0
})
server.on(
'FSTAT',
mustCall((id, handle) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.attrs(id, attrs_)
server.end()
})
)
client.fstat(
handle_,
mustCall((err, attrs) => {
assert(!err, `Unexpected fstat() error: ${err}`)
assert.deepStrictEqual(attrs, attrs_, 'attrs mismatch')
})
)
})
)
setup(
'setstat',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
const attrs_ = new Stats({
uid: 9001,
gid: 9001,
atime: (Date.now() / 1000) | 0,
mtime: (Date.now() / 1000) | 0
})
server.on(
'SETSTAT',
mustCall((id, path, attrs) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
assert.deepStrictEqual(attrs, attrs_, 'attrs mismatch')
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.setstat(
path_,
attrs_,
mustCall((err) => {
assert(!err, `Unexpected setstat() error: ${err}`)
})
)
})
)
setup(
'fsetstat',
mustCall((client, server) => {
const handle_ = Buffer.from('node.js')
const attrs_ = new Stats({
uid: 9001,
gid: 9001,
atime: (Date.now() / 1000) | 0,
mtime: (Date.now() / 1000) | 0
})
server.on(
'FSETSTAT',
mustCall((id, handle, attrs) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert.deepStrictEqual(attrs, attrs_, 'attrs mismatch')
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.fsetstat(
handle_,
attrs_,
mustCall((err) => {
assert(!err, `Unexpected fsetstat() error: ${err}`)
})
)
})
)
setup(
'opendir',
mustCall((client, server) => {
const handle_ = Buffer.from('node.js')
const path_ = '/tmp'
server.on(
'OPENDIR',
mustCall((id, path) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
server.handle(id, handle_)
server.end()
})
)
client.opendir(
path_,
mustCall((err, handle) => {
assert(!err, `Unexpected opendir() error: ${err}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
})
)
})
)
setup(
'readdir',
mustCall((client, server) => {
const handle_ = Buffer.from('node.js')
const list_ = [
{
filename: '.',
longname: 'drwxr-xr-x 56 nodejs nodejs 4096 Nov 10 01:05 .',
attrs: new Stats({
mode: 0o755 | constants.S_IFDIR,
size: 4096,
uid: 9001,
gid: 8001,
atime: 1415599549,
mtime: 1415599590
})
},
{
filename: '..',
longname: 'drwxr-xr-x 4 root root 4096 May 16 2013 ..',
attrs: new Stats({
mode: 0o755 | constants.S_IFDIR,
size: 4096,
uid: 0,
gid: 0,
atime: 1368729954,
mtime: 1368729999
})
},
{
filename: 'foo',
longname: 'drwxrwxrwx 2 nodejs nodejs 4096 Mar 8 2009 foo',
attrs: new Stats({
mode: 0o777 | constants.S_IFDIR,
size: 4096,
uid: 9001,
gid: 8001,
atime: 1368729954,
mtime: 1368729999
})
},
{
filename: 'bar',
longname: '-rw-r--r-- 1 nodejs nodejs 513901992 Dec 4 2009 bar',
attrs: new Stats({
mode: 0o644 | constants.S_IFREG,
size: 513901992,
uid: 9001,
gid: 8001,
atime: 1259972199,
mtime: 1259972199
})
}
]
server.on(
'READDIR',
mustCall((id, handle) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.name(id, list_)
server.end()
})
)
client.readdir(
handle_,
mustCall((err, list) => {
assert(!err, `Unexpected readdir() error: ${err}`)
assert.deepStrictEqual(list, list_.slice(2), 'dir list mismatch')
})
)
})
)
setup(
'readdir (full)',
mustCall((client, server) => {
const handle_ = Buffer.from('node.js')
const list_ = [
{
filename: '.',
longname: 'drwxr-xr-x 56 nodejs nodejs 4096 Nov 10 01:05 .',
attrs: new Stats({
mode: 0o755 | constants.S_IFDIR,
size: 4096,
uid: 9001,
gid: 8001,
atime: 1415599549,
mtime: 1415599590
})
},
{
filename: '..',
longname: 'drwxr-xr-x 4 root root 4096 May 16 2013 ..',
attrs: new Stats({
mode: 0o755 | constants.S_IFDIR,
size: 4096,
uid: 0,
gid: 0,
atime: 1368729954,
mtime: 1368729999
})
},
{
filename: 'foo',
longname: 'drwxrwxrwx 2 nodejs nodejs 4096 Mar 8 2009 foo',
attrs: new Stats({
mode: 0o777 | constants.S_IFDIR,
size: 4096,
uid: 9001,
gid: 8001,
atime: 1368729954,
mtime: 1368729999
})
},
{
filename: 'bar',
longname: '-rw-r--r-- 1 nodejs nodejs 513901992 Dec 4 2009 bar',
attrs: new Stats({
mode: 0o644 | constants.S_IFREG,
size: 513901992,
uid: 9001,
gid: 8001,
atime: 1259972199,
mtime: 1259972199
})
}
]
server.on(
'READDIR',
mustCall((id, handle) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.name(id, list_)
server.end()
})
)
client.readdir(
handle_,
{ full: true },
mustCall((err, list) => {
assert(!err, `Unexpected readdir() error: ${err}`)
assert.deepStrictEqual(list, list_, 'dir list mismatch')
})
)
})
)
setup(
'readdir (EOF)',
mustCall((client, server) => {
const handle_ = Buffer.from('node.js')
server.on(
'READDIR',
mustCall((id, handle) => {
assert(id === 0, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.status(id, STATUS_CODE.EOF)
server.end()
})
)
client.readdir(
handle_,
mustCall((err, list) => {
assert(err && err.code === STATUS_CODE.EOF, `Expected EOF, got: ${err}`)
})
)
})
)
setup(
'unlink',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
server.on(
'REMOVE',
mustCall((id, path) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.unlink(
path_,
mustCall((err) => {
assert(!err, `Unexpected unlink() error: ${err}`)
})
)
})
)
setup(
'mkdir',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
server.on(
'MKDIR',
mustCall((id, path) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.mkdir(
path_,
mustCall((err) => {
assert(!err, `Unexpected mkdir() error: ${err}`)
})
)
})
)
setup(
'rmdir',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
server.on(
'RMDIR',
mustCall((id, path) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.rmdir(
path_,
mustCall((err) => {
assert(!err, `Unexpected rmdir() error: ${err}`)
})
)
})
)
setup(
'realpath',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
const name_ = { filename: '/tmp/foo' }
server.on(
'REALPATH',
mustCall((id, path) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
server.name(id, name_)
server.end()
})
)
client.realpath(
path_,
mustCall((err, name) => {
assert(!err, `Unexpected realpath() error: ${err}`)
assert.deepStrictEqual(name, name_.filename, 'name mismatch')
})
)
})
)
setup(
'stat',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
const attrs_ = new Stats({
mode: 0o644 | constants.S_IFREG,
size: 10 * 1024,
uid: 9001,
gid: 9001,
atime: (Date.now() / 1000) | 0,
mtime: (Date.now() / 1000) | 0
})
server.on(
'STAT',
mustCall((id, path) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
server.attrs(id, attrs_)
server.end()
})
)
client.stat(
path_,
mustCall((err, attrs) => {
assert(!err, `Unexpected stat() error: ${err}`)
assert.deepStrictEqual(attrs, attrs_, 'attrs mismatch')
const expectedTypes = {
isDirectory: false,
isFile: true,
isBlockDevice: false,
isCharacterDevice: false,
isSymbolicLink: false,
isFIFO: false,
isSocket: false
}
for (const [fn, expect] of Object.entries(expectedTypes))
assert(attrs[fn]() === expect, `attrs.${fn}() failed`)
})
)
})
)
setup(
'rename',
mustCall((client, server) => {
const oldPath_ = '/foo/bar/baz'
const newPath_ = '/tmp/foo'
server.on(
'RENAME',
mustCall((id, oldPath, newPath) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(oldPath === oldPath_, `Wrong old path: ${oldPath}`)
assert(newPath === newPath_, `Wrong new path: ${newPath}`)
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.rename(
oldPath_,
newPath_,
mustCall((err) => {
assert(!err, `Unexpected rename() error: ${err}`)
})
)
})
)
setup(
'readlink',
mustCall((client, server) => {
const linkPath_ = '/foo/bar/baz'
const name = { filename: '/tmp/foo' }
server.on(
'READLINK',
mustCall((id, linkPath) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(linkPath === linkPath_, `Wrong link path: ${linkPath}`)
server.name(id, name)
server.end()
})
)
client.readlink(
linkPath_,
mustCall((err, targetPath) => {
assert(!err, `Unexpected readlink() error: ${err}`)
assert(targetPath === name.filename, `Wrong target path: ${targetPath}`)
})
)
})
)
setup(
'symlink',
mustCall((client, server) => {
const linkPath_ = '/foo/bar/baz'
const targetPath_ = '/tmp/foo'
server.on(
'SYMLINK',
mustCall((id, linkPath, targetPath) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(linkPath === linkPath_, `Wrong link path: ${linkPath}`)
assert(targetPath === targetPath_, `Wrong target path: ${targetPath}`)
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.symlink(
targetPath_,
linkPath_,
mustCall((err) => {
assert(!err, `Unexpected symlink() error: ${err}`)
})
)
})
)
setup(
'readFile',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
const handle_ = Buffer.from('hi mom!')
const data_ = Buffer.from('hello world')
server
.on(
'OPEN',
mustCall((id, path, pflags, attrs) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
assert(pflags === OPEN_MODE.READ, `Wrong flags: ${flagsToHuman(pflags)}`)
server.handle(id, handle_)
})
)
.on(
'FSTAT',
mustCall((id, handle) => {
assert(id === 1, `Wrong request id: ${id}`)
const attrs = new Stats({
size: data_.length,
uid: 9001,
gid: 9001,
atime: (Date.now() / 1000) | 0,
mtime: (Date.now() / 1000) | 0
})
server.attrs(id, attrs)
})
)
.on(
'READ',
mustCall((id, handle, offset, len) => {
assert(id === 2, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert(offset === 0, `Wrong read offset: ${offset}`)
server.data(id, data_)
})
)
.on(
'CLOSE',
mustCall((id, handle) => {
assert(id === 3, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.readFile(
path_,
mustCall((err, buf) => {
assert(!err, `Unexpected error: ${err}`)
assert.deepStrictEqual(buf, data_, 'data mismatch')
})
)
})
)
setup(
'readFile (no size from fstat)',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
const handle_ = Buffer.from('hi mom!')
const data_ = Buffer.from('hello world')
let reads = 0
server
.on(
'OPEN',
mustCall((id, path, pflags, attrs) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
assert(pflags === OPEN_MODE.READ, `Wrong flags: ${flagsToHuman(pflags)}`)
server.handle(id, handle_)
})
)
.on(
'FSTAT',
mustCall((id, handle) => {
assert(id === 1, `Wrong request id: ${id}`)
const attrs = new Stats({
uid: 9001,
gid: 9001,
atime: (Date.now() / 1000) | 0,
mtime: (Date.now() / 1000) | 0
})
server.attrs(id, attrs)
})
)
.on(
'READ',
mustCall((id, handle, offset, len) => {
assert(++reads + 1 === id, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
switch (id) {
case 2:
assert(offset === 0, `Wrong read offset for first read: ${offset}`)
server.data(id, data_)
break
case 3:
assert(offset === data_.length, `Wrong read offset for second read: ${offset}`)
server.status(id, STATUS_CODE.EOF)
break
}
}, 2)
)
.on(
'CLOSE',
mustCall((id, handle) => {
assert(id === 4, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.status(id, STATUS_CODE.OK)
server.end()
})
)
client.readFile(
path_,
mustCall((err, buf) => {
assert(!err, `Unexpected error: ${err}`)
assert.deepStrictEqual(buf, data_, 'data mismatch')
})
)
})
)
setup(
'ReadStream',
mustCall((client, server) => {
let reads = 0
const path_ = '/foo/bar/baz'
const handle_ = Buffer.from('hi mom!')
const data_ = Buffer.from('hello world')
server
.on(
'OPEN',
mustCall((id, path, pflags, attrs) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
assert(pflags === OPEN_MODE.READ, `Wrong flags: ${flagsToHuman(pflags)}`)
server.handle(id, handle_)
})
)
.on(
'READ',
mustCall((id, handle, offset, len) => {
assert(id === ++reads, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
if (reads === 1) {
assert(offset === 0, `Wrong read offset: ${offset}`)
server.data(id, data_)
} else {
server.status(id, STATUS_CODE.EOF)
}
}, 2)
)
.on(
'CLOSE',
mustCall((id, handle) => {
assert(id === 3, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.status(id, STATUS_CODE.OK)
server.end()
})
)
let buf = []
client
.createReadStream(path_)
.on(
'readable',
mustCallAtLeast(function () {
let chunk
while ((chunk = this.read()) !== null) buf.push(chunk)
})
)
.on(
'end',
mustCall(() => {
buf = Buffer.concat(buf)
assert.deepStrictEqual(buf, data_, 'data mismatch')
})
)
})
)
setup(
'ReadStream (fewer bytes than requested)',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
const handle_ = Buffer.from('hi mom!')
const data_ = Buffer.from('hello world')
server
.on(
'OPEN',
mustCall((id, path, pflags, attrs) => {
server.handle(id, handle_)
})
)
.on(
'READ',
mustCallAtLeast((id, handle, offset, len) => {
if (offset > data_.length) {
server.status(id, STATUS_CODE.EOF)
} else {
// Only read 4 bytes at a time
server.data(id, data_.slice(offset, offset + 4))
}
})
)
.on(
'CLOSE',
mustCall((id, handle) => {
server.status(id, STATUS_CODE.OK)
server.end()
})
)
let buf = []
client
.createReadStream(path_)
.on(
'readable',
mustCallAtLeast(function () {
let chunk
while ((chunk = this.read()) !== null) buf.push(chunk)
})
)
.on(
'end',
mustCall(() => {
buf = Buffer.concat(buf)
assert.deepStrictEqual(buf, data_, 'data mismatch')
})
)
})
)
setup(
'ReadStream (error)',
mustCall((client, server) => {
const path_ = '/foo/bar/baz'
server.on(
'OPEN',
mustCall((id, path, pflags, attrs) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
assert(pflags === OPEN_MODE.READ, `Wrong flags: ${flagsToHuman(pflags)}`)
server.status(id, STATUS_CODE.NO_SUCH_FILE)
server.end()
})
)
client.createReadStream(path_).on(
'error',
mustCall((err) => {
assert(err.code === STATUS_CODE.NO_SUCH_FILE)
})
)
})
)
setup(
'WriteStream',
mustCall((client, server) => {
let writes = 0
const path_ = '/foo/bar/baz'
const handle_ = Buffer.from('hi mom!')
const data_ = Buffer.from('hello world')
const pflags_ = OPEN_MODE.TRUNC | OPEN_MODE.CREAT | OPEN_MODE.WRITE
server
.on(
'OPEN',
mustCall((id, path, pflags, attrs) => {
assert(id === 0, `Wrong request id: ${id}`)
assert(path === path_, `Wrong path: ${path}`)
assert(pflags === pflags_, `Wrong flags: ${flagsToHuman(pflags)}`)
server.handle(id, handle_)
})
)
.on(
'FSETSTAT',
mustCall((id, handle, attrs) => {
assert(id === 1, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert.strictEqual(attrs.mode, 0o666, 'Wrong file mode')
server.status(id, STATUS_CODE.OK)
})
)
.on(
'WRITE',
mustCall((id, handle, offset, data) => {
assert(id === ++writes + 1, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
assert(offset === (writes - 1) * data_.length, `Wrong write offset: ${offset}`)
assert.deepStrictEqual(data, data_, 'Wrong data')
server.status(id, STATUS_CODE.OK)
}, 3)
)
.on(
'CLOSE',
mustCall((id, handle) => {
assert(id === 5, `Wrong request id: ${id}`)
assert.deepStrictEqual(handle, handle_, 'handle mismatch')
server.status(id, STATUS_CODE.OK)
server.end()
})
)
const writer = client.createWriteStream(path_)
writer.cork && writer.cork()
writer.write(data_)
writer.write(data_)
writer.write(data_)
writer.uncork && writer.uncork()
writer.end()
})
)
{
const { client, server } = setup_('SFTP server aborts with exit-status', {
client: { username: 'foo', password: 'bar' },
server: { hostKeys: [fixture('ssh_host_rsa_key')] }
})
server.on(
'connection',
mustCall((conn) => {
conn
.on(
'authentication',
mustCall((ctx) => {
ctx.accept()
})
)
.on(
'ready',
mustCall(() => {
conn.on(
'session',
mustCall((accept, reject) => {
accept().on(
'sftp',
mustCall((accept, reject) => {
const sftp = accept()
// XXX: hack
sftp._protocol.exitStatus(sftp.outgoing.id, 127)
sftp._protocol.channelClose(sftp.outgoing.id)
})
)
})
)
})
)
})
)
client.on(
'ready',
mustCall(() => {
const timeout = setTimeout(mustNotCall(), 1000)
client.sftp(
mustCall((err, sftp) => {
clearTimeout(timeout)
assert(err, 'Expected error')
assert(err.code === 127, `Expected exit code 127, saw: ${err.code}`)
client.end()
})
)
})
)
}
{
const { client, server } = setup_('SFTP client sets environment', {
client: { username: 'foo', password: 'bar' },
server: { hostKeys: [fixture('ssh_host_rsa_key')] }
})
const env = { SSH2NODETEST: 'foo' }
server.on(
'connection',
mustCall((conn) => {
conn
.on(
'authentication',
mustCall((ctx) => {
ctx.accept()
})
)
.on(
'ready',
mustCall(() => {
conn.on(
'session',
mustCall((accept, reject) => {
accept()
.on(
'env',
mustCall((accept, reject, info) => {
accept && accept()
assert(info.key === Object.keys(env)[0], 'Wrong env key')
assert(info.val === Object.values(env)[0], 'Wrong env value')
})
)
.on(
'sftp',
mustCall((accept, reject) => {
accept()
})
)
})
)
})
)
})
)
client.on(
'ready',
mustCall(() => {
const timeout = setTimeout(mustNotCall(), 1000)
client.sftp(
env,
mustCall((err, sftp) => {
clearTimeout(timeout)
assert(!err, `Unexpected exec error: ${err}`)
client.end()
})
)
})
)
}
// =============================================================================
function setup(title, cb) {
const { client, server } = setupSimple(DEBUG, title)
let clientSFTP
let serverSFTP
const onSFTP = mustCall(() => {
if (clientSFTP && serverSFTP) cb(clientSFTP, serverSFTP)
}, 2)
client.on(
'ready',
mustCall(() => {
client.sftp(
mustCall((err, sftp) => {
assert(!err, `[${title}] Unexpected client sftp start error: ${err}`)
sftp.on(
'close',
mustCall(() => {
client.end()
})
)
clientSFTP = sftp
onSFTP()
})
)
})
)
server.on(
'connection',
mustCall((conn) => {
conn.on(
'ready',
mustCall(() => {
conn.on(
'session',
mustCall((accept, reject) => {
accept().on(
'sftp',
mustCall((accept, reject) => {
const sftp = accept()
sftp.on(
'close',
mustCall(() => {
conn.end()
})
)
serverSFTP = sftp
onSFTP()
})
)
})
)
})
)
})
)
}
function flagsToHuman(flags) {
const ret = []
for (const [name, value] of Object.entries(OPEN_MODE)) {
if (flags & value) ret.push(name)
}
return ret.join(' | ')
}