Crates.io | async-ssh2-russh |
lib.rs | async-ssh2-russh |
version | 0.1.0 |
created_at | 2025-04-29 20:30:33.443319+00 |
updated_at | 2025-04-29 20:30:33.443319+00 |
description | Lighweight async ssh2 client, wrapping russh. |
homepage | |
repository | https://github.com/hydro-project/async-ssh2-russh |
max_upload_size | |
id | 1654032 |
size | 76,319 |
async-ssh2-russh
An asynchronous SSH client wrapper around russh
.
Thin wrapper around russh
, providing a few additional features:
stdout
and stderr
separately.stdin
.AsyncSession::open_sftp
] for SFTP support via russh-sftp
(requires the sftp
feature).Add the following to your Cargo.toml
:
[dependencies]
async-ssh2-russh = "..."
This crate provides two main types, [AsyncSession
] and [AsyncChannel
]. A session represents a connection to an SSH
server, while a channel represents a single communication thread within that session, for example for executing a
command, or opening an SFTP session, etc. These two structs are thin wrappers around [russh::client::Handler
] and
[russh::ChannelWriteHalf
], respectively, with additional methods for asynchronous stream and event handling. They each
implement [Deref
] for their underlying types, so you can use them as if they were the original types.
# let _ = async {
# let my_target_addr = "127.0.0.1:22";
# let my_ssh_user = "user";
# let my_ssh_key_path = "/path/to/private/key";
use async_ssh2_russh::AsyncSession;
use async_ssh2_russh::russh::client::Config;
use async_ssh2_russh::russh::compression;
use async_ssh2_russh::tokio::io::AsyncBufReadExt;
// Configure to try to use compression, for example.
let mut config = Config::default();
config.preferred.compression = (&[
compression::ZLIB,
compression::ZLIB_LEGACY,
compression::NONE,
]).into();
// Connect and authenticate to the SSH server using public key authentication.
let session = AsyncSession::connect_publickey(
config,
my_target_addr,
my_ssh_user,
my_ssh_key_path,
).await.unwrap();
let mut channel = session.open_channel().await.unwrap();
// Connect stdout before running the command, to ensure no output is lost.
let mut stdout = channel.stdout().lines();
channel.exec(false, "echo 'Hello, world!'").await.unwrap();
while let Some(line) = stdout.next_line().await.unwrap() {
println!("Command output: {}", line);
}
println!("Command finished with exit status: {:?}", channel.recv_exit_status().wait().await);
// Send close to server.
channel.close().await.unwrap();
// Wait to receive close back.
channel.wait_close().await;
# };
russh
directly? russh
does not provide easy ways to read from
stdout
and stderr
separately, or to wait for specific events like exit status codes or EOF. Perhaps this
functionality will be subsumed by russh
in the future and make this crate obsolete.async-ssh2-tokio
? async-ssh2-tokio
is also a wrapper around
russh
, but only provides a monolithic execute
method which prevents asynchronous/dynamic interaction with the command's stdout
, stderr
, and stdin
.async-ssh2-lite
? async-ssh2-lite
is a wrapper around the
libssh2
C library, causing additional build complexity and compile time. However async-ssh2-lite
's APIs are very
similar to this crate's, and likely more complete.