Document Dosh Rust SDK examples
This commit is contained in:
@@ -7,6 +7,7 @@ It runs a `dosh-server` on the remote machine and a `dosh` client locally. The
|
|||||||
first setup can use SSH. After that, Dosh can attach over encrypted UDP with
|
first setup can use SSH. After that, Dosh can attach over encrypted UDP with
|
||||||
cached credentials, keep terminal sessions alive, reconnect after network
|
cached credentials, keep terminal sessions alive, reconnect after network
|
||||||
changes, and forward TCP ports.
|
changes, and forward TCP ports.
|
||||||
|
It also includes native encrypted file copy over the Dosh stream layer.
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
@@ -15,8 +16,8 @@ changes, and forward TCP ports.
|
|||||||
- Windows: client only
|
- Windows: client only
|
||||||
- UDP port: one configured server port, default `50000`
|
- UDP port: one configured server port, default `50000`
|
||||||
|
|
||||||
Dosh is not an SCP/SFTP client and does not implement X11 forwarding. Windows
|
Dosh does not implement SFTP, X11 forwarding, or a Windows server. Windows is
|
||||||
is client-only.
|
client-only.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@@ -44,6 +45,12 @@ irm https://git.palav.dev/Palav/dosh/raw/branch/main/install.ps1 | iex
|
|||||||
dosh setup HOST # import SSH config and trust the Dosh host key
|
dosh setup HOST # import SSH config and trust the Dosh host key
|
||||||
dosh HOST # connect
|
dosh HOST # connect
|
||||||
dosh HOST COMMAND # connect and run a command
|
dosh HOST COMMAND # connect and run a command
|
||||||
|
dosh exec HOST COMMAND # run a non-interactive remote command
|
||||||
|
dosh cp SRC DST # copy files; use host:path for a remote side
|
||||||
|
dosh ls host:path # list a remote path
|
||||||
|
dosh cat host:path # print a remote file
|
||||||
|
dosh mkdir host:path # create a remote directory
|
||||||
|
dosh rm [-r] host:path # remove a remote file or directory
|
||||||
dosh update # update Dosh
|
dosh update # update Dosh
|
||||||
dosh status HOST # show remote tmux sessions and Dosh service status
|
dosh status HOST # show remote tmux sessions and Dosh service status
|
||||||
dosh restart HOST # restart dosh-server and show service status
|
dosh restart HOST # restart dosh-server and show service status
|
||||||
@@ -56,12 +63,74 @@ Examples:
|
|||||||
```sh
|
```sh
|
||||||
dosh server
|
dosh server
|
||||||
dosh server tm
|
dosh server tm
|
||||||
|
dosh exec server 'uname -a'
|
||||||
|
dosh cp file.txt server:tmp/file.txt
|
||||||
|
dosh cp -r server:Projects/app ./app
|
||||||
|
dosh cp -r server:Projects/app backup:app
|
||||||
|
dosh ls server:Projects
|
||||||
|
dosh cat server:tmp/file.txt
|
||||||
dosh forward server -L 8080:127.0.0.1:80
|
dosh forward server -L 8080:127.0.0.1:80
|
||||||
dosh forward server -D 1080
|
dosh forward server -D 1080
|
||||||
dosh forward server -R 2222:127.0.0.1:22
|
dosh forward server -R 2222:127.0.0.1:22
|
||||||
```
|
```
|
||||||
|
|
||||||
Agent forwarding is opt-in with `-A` and must be enabled on the server.
|
Agent forwarding is opt-in with `-A` and must be enabled on the server.
|
||||||
|
File copy is enabled by `allow_file_transfer = true` on the server.
|
||||||
|
|
||||||
|
## Library
|
||||||
|
|
||||||
|
Dosh can also be used as a Rust transport for application protocols that need
|
||||||
|
encrypted streams, roaming, reconnect behavior, keepalives, retransmission,
|
||||||
|
flow control, and in-order delivery.
|
||||||
|
|
||||||
|
Use `dosh::client::DoshClient` and `dosh::server::DoshServer` for the complete
|
||||||
|
native-auth path. Use `dosh::transport::DoshTransport` or `StreamMux` only when
|
||||||
|
you already have your own session/auth layer.
|
||||||
|
|
||||||
|
Client-side SDK:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let client = dosh::client::DoshClient::load()?;
|
||||||
|
let mut dosh = client
|
||||||
|
.connect("server")
|
||||||
|
.service("myapp")
|
||||||
|
.connect()
|
||||||
|
.await?
|
||||||
|
.into_transport();
|
||||||
|
|
||||||
|
let stream = dosh.open_service("myapp").await?;
|
||||||
|
dosh.send(stream, b"hello").await?;
|
||||||
|
```
|
||||||
|
|
||||||
|
Server-side SDK:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let config = dosh::server::DoshServerConfig::default()
|
||||||
|
.service("myapp")?;
|
||||||
|
let mut server = dosh::server::DoshServer::bind(config).await?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match server.recv().await? {
|
||||||
|
dosh::server::DoshServerEvent::Accepted(client) => {
|
||||||
|
eprintln!("accepted {:?}", client.conn_id);
|
||||||
|
}
|
||||||
|
dosh::server::DoshServerEvent::Session { conn_id, event } => {
|
||||||
|
if let dosh::transport::SessionEvent::Stream(
|
||||||
|
dosh::transport::TransportEvent::Open(open)
|
||||||
|
) = event {
|
||||||
|
server.accept_stream(conn_id, open.stream_id).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dosh::server::DoshServerEvent::Ignored => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The server runtime owns UDP reads and demuxes packets by connection id, so
|
||||||
|
multiple clients can share one Dosh port without per-session readers racing.
|
||||||
|
|
||||||
|
Runnable SDK examples live in `examples/sdk_echo_server.rs` and
|
||||||
|
`examples/sdk_echo_client.rs`.
|
||||||
|
|
||||||
## Config
|
## Config
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
use anyhow::{Result, bail};
|
||||||
|
use dosh::client::DoshClient;
|
||||||
|
use dosh::transport::{SessionEvent, TransportEvent};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
let mut args = std::env::args().skip(1);
|
||||||
|
let host = args.next().unwrap_or_else(|| "server".to_string());
|
||||||
|
let message = args.collect::<Vec<_>>().join(" ");
|
||||||
|
let message = if message.is_empty() {
|
||||||
|
"hello from dosh".to_string()
|
||||||
|
} else {
|
||||||
|
message
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = DoshClient::load()?;
|
||||||
|
let mut transport = client
|
||||||
|
.connect(host)
|
||||||
|
.service("echo")
|
||||||
|
.connect()
|
||||||
|
.await?
|
||||||
|
.into_transport();
|
||||||
|
let stream = transport.open_service("echo").await?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match transport.recv().await? {
|
||||||
|
SessionEvent::Stream(TransportEvent::OpenOk { stream_id, .. })
|
||||||
|
if stream_id == stream =>
|
||||||
|
{
|
||||||
|
transport.send(stream, message.as_bytes().to_vec()).await?;
|
||||||
|
}
|
||||||
|
SessionEvent::Stream(TransportEvent::Data(data)) if data.stream_id == stream => {
|
||||||
|
for chunk in data.chunks {
|
||||||
|
print!("{}", String::from_utf8_lossy(&chunk));
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
transport.close(stream).await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
SessionEvent::Stream(TransportEvent::OpenReject { reason, .. }) => {
|
||||||
|
bail!("server rejected echo stream: {reason}");
|
||||||
|
}
|
||||||
|
SessionEvent::Stream(_)
|
||||||
|
| SessionEvent::Ping
|
||||||
|
| SessionEvent::Pong
|
||||||
|
| SessionEvent::Ignored => {}
|
||||||
|
}
|
||||||
|
transport.maintenance().await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use dosh::server::{DoshServer, DoshServerConfig, DoshServerEvent};
|
||||||
|
use dosh::transport::{SessionEvent, TransportEvent};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
let config = DoshServerConfig::default().service("echo")?;
|
||||||
|
let mut server = DoshServer::bind(config).await?;
|
||||||
|
eprintln!("listening on {}", server.local_addr()?);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match server.recv().await? {
|
||||||
|
DoshServerEvent::Accepted(client) => {
|
||||||
|
eprintln!(
|
||||||
|
"accepted user={} session={} conn={:?}",
|
||||||
|
client.user, client.session, client.conn_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
DoshServerEvent::Session {
|
||||||
|
conn_id,
|
||||||
|
event: SessionEvent::Stream(TransportEvent::Open(open)),
|
||||||
|
} => {
|
||||||
|
server.accept_stream(conn_id, open.stream_id).await?;
|
||||||
|
}
|
||||||
|
DoshServerEvent::Session {
|
||||||
|
conn_id,
|
||||||
|
event: SessionEvent::Stream(TransportEvent::Data(data)),
|
||||||
|
} => {
|
||||||
|
for chunk in data.chunks {
|
||||||
|
server.send(conn_id, data.stream_id, chunk).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DoshServerEvent::Session { .. } | DoshServerEvent::Ignored => {}
|
||||||
|
}
|
||||||
|
server.maintenance().await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user