Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ examples/http/poll_loop.py
examples/tcp-p3/tcp.wasm
examples/tcp/tcp.wasm
examples/tcp/command
examples/tls-p3/tls.wasm
examples/cli-p3/cli.wasm
examples/cli/cli.wasm
examples/cli/command
Expand Down
38 changes: 38 additions & 0 deletions examples/tls-p3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Example: `tls-p3`

This is an example of how to use [componentize-py] and [Wasmtime] to build and
run a Python-based TLS client component targetting version `0.3.0-rc-2026-02-09`
of the [wasi-cli] `command` world with [wasi-tls] and [wasi-sockets] support.

[componentize-py]: https://github.com/bytecodealliance/componentize-py
[Wasmtime]: https://github.com/bytecodealliance/wasmtime
[wasi-cli]: https://github.com/WebAssembly/WASI/tree/v0.3.0-rc-2026-02-09/proposals/cli/wit-0.3.0-draft
[wasi-sockets]: https://github.com/WebAssembly/WASI/tree/v0.3.0-rc-2026-02-09/proposals/sockets/wit-0.3.0-draft
[wasi-tls]: https://github.com/WebAssembly/wasi-tls

## Prerequisites

* `Wasmtime` 43.0.0
* `componentize-py` 0.21.0

Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If
you don't have `cargo`, you can download and install from
https://github.com/bytecodealliance/wasmtime/releases/tag/v43.0.0.

```
cargo install --version 43.0.0 wasmtime-cli
pip install componentize-py==0.21.0
```

## Running the demo

```
componentize-py -d ../../wit -w tls-p3 componentize app -o tls.wasm
wasmtime run -Sp3,inherit-network,tls,allow-ip-name-lookup -Wcomponent-model-async tls.wasm <server_name>
```

For example, to connect to `api.github.com` over TLS:

```
wasmtime run -Sp3,inherit-network,tls,allow-ip-name-lookup -Wcomponent-model-async tls.wasm api.github.com
```
76 changes: 76 additions & 0 deletions examples/tls-p3/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import sys
import asyncio
import wit_world
from wit_world import exports
from wit_world.imports.wasi_sockets_types import (
TcpSocket,
IpSocketAddress_Ipv4,
IpSocketAddress_Ipv6,
Ipv4SocketAddress,
Ipv6SocketAddress,
IpAddressFamily,
IpAddress_Ipv4,
IpAddress_Ipv6,
)
from wit_world.imports.ip_name_lookup import resolve_addresses
from wit_world.imports.client import Connector


class Run(exports.Run):
async def run(self) -> None:
args = sys.argv[1:]
if len(args) != 1:
print("usage: tls-p3 <server_name>", file=sys.stderr)
exit(-1)

server_name = args[0]
await send_and_receive(server_name)


async def send_and_receive(server_name: str) -> None:
port = 443
addresses = await resolve_addresses(server_name)
address = addresses[0]

if isinstance(address, IpAddress_Ipv4):
family = IpAddressFamily.IPV4
sock_addr: IpSocketAddress_Ipv4 | IpSocketAddress_Ipv6 = IpSocketAddress_Ipv4(
Ipv4SocketAddress(port=port, address=address.value)
)
else:
family = IpAddressFamily.IPV6
sock_addr = IpSocketAddress_Ipv6(
Ipv6SocketAddress(port=port, flow_info=0, address=address.value, scope_id=0)
)

sock = TcpSocket.create(family)
await sock.connect(sock_addr)

tls = Connector()

data_send_tx, data_send_rx = wit_world.byte_stream()
tls_send_rx, tls_send_fut = tls.send(data_send_rx)
sock_send_fut = sock.send(tls_send_rx)

tls_recv_rx, sock_recv_fut = sock.receive()
data_recv_rx, tls_recv_fut = tls.receive(tls_recv_rx)

async def write() -> None:
with data_send_tx:
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())

async def read() -> None:
with data_recv_rx:
while not data_recv_rx.writer_dropped:
buf = await data_recv_rx.read(1024)
sys.stdout.buffer.write(buf)

await asyncio.gather(
Connector.connect(tls, server_name),
write(),
read(),
sock_send_fut.read(),
sock_recv_fut.read(),
tls_send_fut.read(),
tls_recv_fut.read(),
)
19 changes: 19 additions & 0 deletions tests/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,25 @@ fn lint_tcp_p3_bindings() -> anyhow::Result<()> {
Ok(())
}

#[test]
fn lint_tls_p3_bindings() -> anyhow::Result<()> {
let dir = tempfile::tempdir()?;
fs_extra::copy_items(
&["./examples/tls-p3", "./wit"],
dir.path(),
&CopyOptions::new(),
)?;
let path = dir.path().join("tls-p3");

generate_bindings(&path, "tls-p3")?;

assert!(predicate::path::is_dir().eval(&path.join("wit_world")));

mypy_check(&path, ["--strict", "-m", "app"]);

Ok(())
}

fn generate_bindings(path: &Path, world: &str) -> Result<Assert, anyhow::Error> {
Ok(cargo::cargo_bin_cmd!("componentize-py")
.current_dir(path)
Expand Down
42 changes: 42 additions & 0 deletions tests/componentize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,48 @@ fn test_tcp_example(name: &str, world: &str) -> anyhow::Result<()> {
Ok(())
}

#[test]
fn tls_p3_example() -> anyhow::Result<()> {
let dir = tempfile::tempdir()?;
fs_extra::copy_items(
&["./examples/tls-p3", "./wit"],
dir.path(),
&CopyOptions::new(),
)?;
let path = dir.path().join("tls-p3");

cargo::cargo_bin_cmd!("componentize-py")
.current_dir(&path)
.args([
"-d",
"../wit",
"-w",
"tls-p3",
"componentize",
"app",
"-o",
"tls.wasm",
])
.assert()
.success()
.stdout("Component built successfully\n");

Command::new("wasmtime")
.current_dir(&path)
.args([
"run",
"-Sp3,inherit-network,tls,allow-ip-name-lookup",
"-Wcomponent-model-async",
"tls.wasm",
"api.github.com",
])
.assert()
.success()
.stdout(predicate::str::starts_with("HTTP/1.1 200 OK"));

Ok(())
}

fn retry<T>(mut func: impl FnMut() -> anyhow::Result<T>) -> anyhow::Result<T> {
let times = 10;
for i in 0..times {
Expand Down
Loading
Loading