SPOA (Stream Processing Offload Agent) library for MoonBit: SPOP protocol codec, agent server (TCP/UDS) and SPOE client
| Package | Purpose | Dependencies |
|---|---|---|
| pangbit/moonspoa/spop | Protocol codecs and HELLO negotiation | MoonBit core only; backend independent |
| pangbit/moonspoa/agent | Agent configuration and session state machine | spop, async runtime and IO; caller supplies transport |
| pangbit/moonspoa/client | HELLO, sequential / pipelined NOTIFY, timeouts, DISCONNECT | spop, async runtime, IO and queues |
| pangbit/moonspoa/server | TCP and Unix domain socket transport | agent, async networking and a C stub; native only |
| pangbit/moonspoa | Convenience facade | Re-exports spop types and agent's Agent / Session; not server or client |
moon add pangbit/moonspoaimport {
"pangbit/moonspoa/agent",
"pangbit/moonspoa/server",
"moonbitlang/async",
}
supported_targets = "native"
pkgtype(kind: "executable")///|
async fn main {
let agent = @agent.Agent::new().on("check-ip", fn(msg) {
// This minimal example accepts the demo client's string value.
// HAProxy's `args ip=src` sends Ipv4/Ipv6; see examples/server.
guard msg.args is [("ip", Str(ip)), ..] else { return [] }
// HAProxy adds the scope and configured variable prefix.
[SetVar(Transaction, "ip_blocked", Bool(ip == "203.0.113.7"))]
})
let server = @server.Server::bind(
agent,
@server.Listener::tcp("127.0.0.1:12345"),
)
defer server.close()
server.run()
}import {
"pangbit/moonspoa/client",
"moonbitlang/async",
"moonbitlang/async/socket",
}
supported_targets = "native"
pkgtype(kind: "executable")///|
async fn main {
let conn = @socket.Tcp::connect(@socket.Addr::parse("127.0.0.1:12345"))
defer conn.close()
let client = @client.Client::hello(conn, conn)
let actions = client.notify_messages([
{ name: "check-ip", args: [("ip", Str("203.0.113.7"))], },
])
for action in actions {
println("\{Repr(action)}")
}
ignore(client.disconnect())
}///|
async fn query_all(client : @client.Client, ips : Array[String]) -> Unit {
client.with_pipelining(fn(client) {
@async.with_task_group(group => {
for ip in ips {
group.spawn_bg(() => {
let actions = client.notify_messages(
[{ name: "check-ip", args: [("ip", Str(ip))], }],
timeout=1000,
)
println("\{Repr(actions)}")
})
}
})
})
}///|
test {
let message : @moonspoa.Message = {
name: "check-ip",
args: [("ip", Str("1.2.3.4"))],
}
let frame = @moonspoa.Frame::notify(0, 1, [message])
debug_inspect(frame.frame_type, content="Notify")
}# Terminal 1: agent server
moon run examples/server -- --port 12345 --block 203.0.113.7
# Terminal 2: send an IP and print the ACK actions and verdict
moon run examples/client -- --port 12345 203.0.113.7spoe-message check-ip
args ip=src
event on-frontend-http-request# Terminal 1: block the local client's address
moon run examples/server -- --block 127.0.0.1
# Terminal 2: paths in the HAProxy configuration are relative to this directory
cd examples/haproxy && haproxy -f haproxy.cfg
# Terminal 3: a matching address should receive 403
curl -i http://127.0.0.1:8080/moon check
moon test # native is the module's preferred target
moon test --release
moon info
moon fmt --check
moon doc
moon package --list # inspect publication contents without uploadingInstall
Download zipSPOA (Stream Processing Offload Agent) library for MoonBit: SPOP protocol codec, agent server (TCP/UDS) and SPOE client