Skip to content

Commit ac201d0

Browse files
KuaaMUrvolosatovs
authored andcommitted
docs: add Unix Domain Socket transport example
Added hello-unix-client and hello-unix-server examples demonstrating wrpc transport over Unix Domain Sockets, matching the existing TCP examples. Closes #481 Signed-off-by: KuaaMU <138859253+KuaaMU@users.noreply.github.com>
1 parent b46bb6d commit ac201d0

13 files changed

Lines changed: 274 additions & 0 deletions

File tree

‎Cargo.lock‎

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "hello-unix-client"
3+
version = "0.1.0"
4+
5+
authors.workspace = true
6+
categories.workspace = true
7+
edition.workspace = true
8+
license.workspace = true
9+
repository.workspace = true
10+
11+
[dependencies]
12+
anyhow = { workspace = true }
13+
clap = { workspace = true, features = [
14+
"color",
15+
"derive",
16+
"error-context",
17+
"help",
18+
"std",
19+
"suggestions",
20+
"usage",
21+
] }
22+
tokio = { workspace = true, features = ["rt-multi-thread"] }
23+
tracing-subscriber = { workspace = true, features = ["ansi", "fmt"] }
24+
wit-bindgen-wrpc = { workspace = true }
25+
wrpc-transport = { workspace = true, features = ["net"] }
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use std::path::PathBuf;
2+
3+
use anyhow::Context as _;
4+
use clap::Parser;
5+
6+
mod bindings {
7+
wit_bindgen_wrpc::generate!({
8+
with: {
9+
"wrpc-examples:hello/handler": generate
10+
}
11+
});
12+
}
13+
14+
#[derive(Parser, Debug)]
15+
#[command(author, version, about, long_about = None)]
16+
struct Args {
17+
/// Path to invoke `wrpc-examples:hello/handler.hello` on
18+
#[arg(default_value = "/tmp/wrpc/hello.sock")]
19+
path: PathBuf,
20+
}
21+
22+
#[tokio::main]
23+
async fn main() -> anyhow::Result<()> {
24+
tracing_subscriber::fmt().init();
25+
26+
let Args { path } = Args::parse();
27+
let wrpc = wrpc_transport::unix::Client::from(path.as_path());
28+
let hello = bindings::wrpc_examples::hello::handler::hello(&wrpc, ())
29+
.await
30+
.context("failed to invoke `wrpc-examples.hello/handler.hello`")?;
31+
eprintln!("{hello}");
32+
Ok(())
33+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[hello]
2+
path = "../../../wit/hello"
3+
sha256 = "3680bb734f3fa9f7325674142a2a9b558efd34ea2cb2df7ccb651ad869078d27"
4+
sha512 = "688fdae594dc43bd65bd15ea66b77a8f97cb4bc1c3629719e91d6c1391c66f7c8c6517d096f686cca996188f64f075c4ccb0d70a40097ce76b8b4bcc71dc7506"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello = "../../../wit/hello"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package wrpc-examples:hello;
2+
3+
interface handler {
4+
hello: func() -> string;
5+
}
6+
7+
world client {
8+
import handler;
9+
}
10+
11+
world server {
12+
export handler;
13+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package wrpc-examples:hello-rust-client;
2+
3+
world client {
4+
include wrpc-examples:hello/client;
5+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
name = "hello-unix-server"
3+
version = "0.1.0"
4+
5+
authors.workspace = true
6+
categories.workspace = true
7+
edition.workspace = true
8+
license.workspace = true
9+
repository.workspace = true
10+
11+
[dependencies]
12+
anyhow = { workspace = true }
13+
clap = { workspace = true, features = [
14+
"color",
15+
"derive",
16+
"error-context",
17+
"help",
18+
"std",
19+
"suggestions",
20+
"usage",
21+
] }
22+
futures = { workspace = true }
23+
tokio = { workspace = true, features = ["fs", "rt-multi-thread", "net", "signal"] }
24+
tracing = { workspace = true }
25+
tracing-subscriber = { workspace = true, features = ["ansi", "fmt"] }
26+
wit-bindgen-wrpc = { workspace = true }
27+
wrpc-transport = { workspace = true, features = ["net"] }
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use core::pin::pin;
2+
3+
use std::path::PathBuf;
4+
use std::sync::Arc;
5+
6+
use anyhow::Context as _;
7+
use clap::Parser;
8+
use futures::stream::select_all;
9+
use futures::StreamExt as _;
10+
use tokio::task::JoinSet;
11+
use tokio::{fs, select, signal};
12+
use tracing::{debug, error, info, warn};
13+
14+
mod bindings {
15+
wit_bindgen_wrpc::generate!({
16+
with: {
17+
"wrpc-examples:hello/handler": generate,
18+
}
19+
});
20+
}
21+
22+
#[derive(Parser, Debug)]
23+
#[command(author, version, about, long_about = None)]
24+
struct Args {
25+
/// Path to serve `wrpc-examples:hello/handler.hello` on
26+
#[arg(default_value = "/tmp/wrpc/hello.sock")]
27+
path: PathBuf,
28+
}
29+
30+
#[derive(Clone, Copy)]
31+
struct Server;
32+
33+
impl bindings::exports::wrpc_examples::hello::handler::Handler<tokio::net::unix::SocketAddr>
34+
for Server
35+
{
36+
async fn hello(&self, _: tokio::net::unix::SocketAddr) -> anyhow::Result<String> {
37+
Ok("hello from Rust".to_string())
38+
}
39+
}
40+
41+
#[tokio::main]
42+
async fn main() -> anyhow::Result<()> {
43+
tracing_subscriber::fmt().init();
44+
45+
let Args { path } = Args::parse();
46+
47+
if let Some(dir) = path.parent() {
48+
if !dir.exists() {
49+
fs::create_dir_all(dir)
50+
.await
51+
.with_context(|| format!("failed to create `{}`", dir.display()))?
52+
}
53+
}
54+
let lis = tokio::net::UnixListener::bind(&path)
55+
.with_context(|| format!("failed to bind Unix listener on `{}`", path.display()))?;
56+
let srv = Arc::new(wrpc_transport::Server::default());
57+
let accept = tokio::spawn({
58+
let srv = Arc::clone(&srv);
59+
async move {
60+
loop {
61+
if let Err(err) = srv.accept(&lis).await {
62+
error!(?err, "failed to accept Unix connection");
63+
}
64+
}
65+
}
66+
});
67+
68+
let invocations = bindings::serve(srv.as_ref(), Server)
69+
.await
70+
.context("failed to serve `wrpc-examples.hello/handler.hello`")?;
71+
// NOTE: This will conflate all invocation streams into a single stream via `futures::stream::SelectAll`,
72+
// to customize this, iterate over the returned `invocations` and set up custom handling per export
73+
let mut invocations = select_all(
74+
invocations
75+
.into_iter()
76+
.map(|(instance, name, invocations)| invocations.map(move |res| (instance, name, res))),
77+
);
78+
let shutdown = signal::ctrl_c();
79+
let mut shutdown = pin!(shutdown);
80+
let mut tasks = JoinSet::new();
81+
loop {
82+
select! {
83+
Some((instance, name, res)) = invocations.next() => {
84+
match res {
85+
Ok(fut) => {
86+
debug!(instance, name, "invocation accepted");
87+
tasks.spawn(async move {
88+
if let Err(err) = fut.await {
89+
warn!(?err, "failed to handle invocation");
90+
} else {
91+
info!(instance, name, "invocation successfully handled");
92+
}
93+
});
94+
}
95+
Err(err) => {
96+
warn!(?err, instance, name, "failed to accept invocation");
97+
}
98+
}
99+
}
100+
Some(res) = tasks.join_next() => {
101+
if let Err(err) = res {
102+
error!(?err, "failed to join task");
103+
}
104+
}
105+
res = &mut shutdown => {
106+
accept.abort();
107+
// wait for all invocations to complete
108+
while let Some(res) = tasks.join_next().await {
109+
if let Err(err) = res {
110+
error!(?err, "failed to join task");
111+
}
112+
}
113+
return res.context("failed to listen for ^C")
114+
}
115+
}
116+
}
117+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[hello]
2+
path = "../../../wit/hello"
3+
sha256 = "3680bb734f3fa9f7325674142a2a9b558efd34ea2cb2df7ccb651ad869078d27"
4+
sha512 = "688fdae594dc43bd65bd15ea66b77a8f97cb4bc1c3629719e91d6c1391c66f7c8c6517d096f686cca996188f64f075c4ccb0d70a40097ce76b8b4bcc71dc7506"

0 commit comments

Comments
 (0)