Skip to content

Commit 59db3f0

Browse files
committed
add tls-p3 example and test
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
1 parent ae8553d commit 59db3f0

File tree

9 files changed

+213
-0
lines changed

9 files changed

+213
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ examples/http/poll_loop.py
1515
examples/tcp-p3/tcp.wasm
1616
examples/tcp/tcp.wasm
1717
examples/tcp/command
18+
examples/tls-p3/tls.wasm
1819
examples/cli-p3/cli.wasm
1920
examples/cli/cli.wasm
2021
examples/cli/command

examples/tls-p3/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Example: `tls-p3`
2+
3+
This is an example of how to use [componentize-py] and [Wasmtime] to build and
4+
run a Python-based TLS client component targetting version `0.3.0-rc-2026-02-09`
5+
of the [wasi-cli] `command` world with [wasi-tls] and [wasi-sockets] support.
6+
7+
[componentize-py]: https://github.com/bytecodealliance/componentize-py
8+
[Wasmtime]: https://github.com/bytecodealliance/wasmtime
9+
[wasi-cli]: https://github.com/WebAssembly/WASI/tree/v0.3.0-rc-2026-02-09/proposals/cli/wit-0.3.0-draft
10+
[wasi-sockets]: https://github.com/WebAssembly/WASI/tree/v0.3.0-rc-2026-02-09/proposals/sockets/wit-0.3.0-draft
11+
[wasi-tls]: https://github.com/WebAssembly/wasi-tls
12+
13+
## Prerequisites
14+
15+
* `Wasmtime` 43.0.0
16+
* `componentize-py` 0.21.0
17+
18+
Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If
19+
you don't have `cargo`, you can download and install from
20+
https://github.com/bytecodealliance/wasmtime/releases/tag/v43.0.0.
21+
22+
```
23+
cargo install --version 43.0.0 wasmtime-cli
24+
pip install componentize-py==0.21.0
25+
```
26+
27+
## Running the demo
28+
29+
```
30+
componentize-py -d ../../wit -w tls-p3 componentize app -o tls.wasm
31+
wasmtime run -Sp3,inherit-network,tls,allow-ip-name-lookup -Wcomponent-model-async tls.wasm <server_name>
32+
```
33+
34+
For example, to connect to `api.github.com` over TLS:
35+
36+
```
37+
wasmtime run -Sp3,inherit-network,tls,allow-ip-name-lookup -Wcomponent-model-async tls.wasm api.github.com
38+
```

examples/tls-p3/app.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import sys
2+
import asyncio
3+
import wit_world
4+
from wit_world import exports
5+
from wit_world.imports.wasi_sockets_types import (
6+
TcpSocket,
7+
IpSocketAddress_Ipv4,
8+
IpSocketAddress_Ipv6,
9+
Ipv4SocketAddress,
10+
Ipv6SocketAddress,
11+
IpAddressFamily,
12+
IpAddress_Ipv4,
13+
IpAddress_Ipv6,
14+
)
15+
from wit_world.imports.ip_name_lookup import resolve_addresses
16+
from wit_world.imports.client import Connector
17+
18+
19+
class Run(exports.Run):
20+
async def run(self) -> None:
21+
args = sys.argv[1:]
22+
if len(args) != 1:
23+
print("usage: tls-p3 <server_name>", file=sys.stderr)
24+
exit(-1)
25+
26+
server_name = args[0]
27+
await send_and_receive(server_name)
28+
29+
30+
async def send_and_receive(server_name: str) -> None:
31+
port = 443
32+
addresses = await resolve_addresses(server_name)
33+
address = addresses[0]
34+
35+
if isinstance(address, IpAddress_Ipv4):
36+
family = IpAddressFamily.IPV4
37+
sock_addr: IpSocketAddress_Ipv4 | IpSocketAddress_Ipv6 = IpSocketAddress_Ipv4(
38+
Ipv4SocketAddress(port=port, address=address.value)
39+
)
40+
else:
41+
family = IpAddressFamily.IPV6
42+
sock_addr = IpSocketAddress_Ipv6(
43+
Ipv6SocketAddress(port=port, flow_info=0, address=address.value, scope_id=0)
44+
)
45+
46+
sock = TcpSocket.create(family)
47+
await sock.connect(sock_addr)
48+
49+
tls = Connector()
50+
51+
data_send_tx, data_send_rx = wit_world.byte_stream()
52+
tls_send_rx, tls_send_fut = tls.send(data_send_rx)
53+
sock_send_fut = sock.send(tls_send_rx)
54+
55+
tls_recv_rx, sock_recv_fut = sock.receive()
56+
data_recv_rx, tls_recv_fut = tls.receive(tls_recv_rx)
57+
58+
async def write() -> None:
59+
with data_send_tx:
60+
await data_send_tx.write_all(f"GET / HTTP/1.1\r\nHost: {server_name}\r\nUser-Agent: wasmtime-wasi-rust\r\nConnection: close\r\n\r\n".encode())
61+
62+
async def read() -> None:
63+
with data_recv_rx:
64+
while not data_recv_rx.writer_dropped:
65+
buf = await data_recv_rx.read(1024)
66+
sys.stdout.buffer.write(buf)
67+
68+
await asyncio.gather(
69+
Connector.connect(tls, server_name),
70+
write(),
71+
read(),
72+
sock_send_fut.read(),
73+
sock_recv_fut.read(),
74+
tls_send_fut.read(),
75+
tls_recv_fut.read(),
76+
)

tests/bindings.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,25 @@ fn lint_tcp_p3_bindings() -> anyhow::Result<()> {
190190
Ok(())
191191
}
192192

193+
#[test]
194+
fn lint_tls_p3_bindings() -> anyhow::Result<()> {
195+
let dir = tempfile::tempdir()?;
196+
fs_extra::copy_items(
197+
&["./examples/tls-p3", "./wit"],
198+
dir.path(),
199+
&CopyOptions::new(),
200+
)?;
201+
let path = dir.path().join("tls-p3");
202+
203+
generate_bindings(&path, "tls-p3")?;
204+
205+
assert!(predicate::path::is_dir().eval(&path.join("wit_world")));
206+
207+
mypy_check(&path, ["--strict", "-m", "app"]);
208+
209+
Ok(())
210+
}
211+
193212
fn generate_bindings(path: &Path, world: &str) -> Result<Assert, anyhow::Error> {
194213
Ok(cargo::cargo_bin_cmd!("componentize-py")
195214
.current_dir(path)

tests/componentize.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,48 @@ fn test_tcp_example(name: &str, world: &str) -> anyhow::Result<()> {
303303
Ok(())
304304
}
305305

306+
#[test]
307+
fn tls_p3_example() -> anyhow::Result<()> {
308+
let dir = tempfile::tempdir()?;
309+
fs_extra::copy_items(
310+
&["./examples/tls-p3", "./wit"],
311+
dir.path(),
312+
&CopyOptions::new(),
313+
)?;
314+
let path = dir.path().join("tls-p3");
315+
316+
cargo::cargo_bin_cmd!("componentize-py")
317+
.current_dir(&path)
318+
.args([
319+
"-d",
320+
"../wit",
321+
"-w",
322+
"tls-p3",
323+
"componentize",
324+
"app",
325+
"-o",
326+
"tls.wasm",
327+
])
328+
.assert()
329+
.success()
330+
.stdout("Component built successfully\n");
331+
332+
Command::new("wasmtime")
333+
.current_dir(&path)
334+
.args([
335+
"run",
336+
"-Sp3,inherit-network,tls,allow-ip-name-lookup",
337+
"-Wcomponent-model-async",
338+
"tls.wasm",
339+
"api.github.com",
340+
])
341+
.assert()
342+
.success()
343+
.stdout(predicate::str::starts_with("HTTP/1.1 200 OK"));
344+
345+
Ok(())
346+
}
347+
306348
fn retry<T>(mut func: impl FnMut() -> anyhow::Result<T>) -> anyhow::Result<T> {
307349
let times = 10;
308350
for i in 0..times {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
interface client {
2+
use types.{error};
3+
4+
resource connector {
5+
constructor();
6+
7+
/// Set up the encryption stream transform.
8+
/// This takes an unprotected `cleartext` application data stream and
9+
/// returns an encrypted data stream, ready to be sent out over the network.
10+
/// Closing the `cleartext` stream will cause a `close_notify` packet to be emitted on the returned output stream.
11+
send: func(cleartext: stream<u8>) -> tuple<stream<u8>, future<result<_, error>>>;
12+
13+
/// Set up the decryption stream transform.
14+
/// This takes an encrypted data stream, as received via e.g. the network,
15+
/// and returns a decrypted application data stream.
16+
receive: func(ciphertext: stream<u8>) -> tuple<stream<u8>, future<result<_, error>>>;
17+
18+
/// Perform the handshake.
19+
/// The `send` & `receive` streams must be set up before calling this method.
20+
connect: static async func(this: connector, server-name: string) -> result<_, error>;
21+
}
22+
}

wit/deps/tls-0.3.0-draft/types.wit

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
interface types {
2+
resource error {
3+
to-debug-string: func() -> string;
4+
}
5+
}

wit/deps/tls-0.3.0-draft/world.wit

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package wasi:tls@0.3.0-draft;
2+
3+
world imports {
4+
import client;
5+
import types;
6+
}

wit/tls.wit

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
world tls-p3 {
2+
include wasi:cli/command@0.3.0-rc-2026-02-09;
3+
include wasi:tls/imports@0.3.0-draft;
4+
}

0 commit comments

Comments
 (0)