Files
zap/network_uds_test.go
zeekayandHanzo Dev c1f5772196 feat(node): listen and dial a unix socket
A Node bound its listener with net.Listen("tcp", ":port") and reached a
peer with net.DialTimeout("tcp", addr), so a socket path was refused at
the dialer — "missing port in address" — and could not be bound at all.
Service-to-service traffic on one host therefore had to take a TCP hop
it does not need.

NodeConfig.Address says where a node listens and overrides Port. Network
derives the network from the address — a path is a unix socket, anything
else is host:port — and the listener and both dial paths share that one
rule, so a node cannot bind one transport and be dialled on another.

A unix socket is unreachable off-host, so a node bound to one does not
advertise over mDNS. A socket file left by a process that did not unlink
it is cleared before bind; one that is still served is refused, so two
live nodes cannot steal each other's address.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 16:20:25 -07:00

74 lines
2.0 KiB
Go

package zap
import (
"context"
"log/slog"
"os"
"path/filepath"
"testing"
)
// A node binds a unix socket and answers a Call over it.
func TestNode_UnixSocketRoundTrip(t *testing.T) {
sock := filepath.Join(t.TempDir(), "tasks.sock")
quiet := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
srv := NewNode(NodeConfig{NodeID: "server", ServiceType: "_tasks._tcp", Address: sock, Logger: quiet, NoDiscovery: true})
if err := srv.Start(); err != nil {
t.Fatalf("listen on %s: %v", sock, err)
}
defer srv.Stop()
if _, err := os.Stat(sock); err != nil {
t.Fatalf("socket not created: %v", err)
}
const op uint16 = 0x0090
srv.Handle(op, func(ctx context.Context, from string, msg *Message) (*Message, error) {
b := NewBuilder(64)
o := b.StartObject(24)
o.SetBytes(0, []byte("pong"))
o.FinishAsRoot()
return Parse(b.FinishWithFlags(uint16(op) << 8))
})
cli := NewNode(NodeConfig{NodeID: "client", ServiceType: "_tasks._tcp", Logger: quiet, NoDiscovery: true})
if err := cli.Start(); err != nil {
t.Fatalf("client start: %v", err)
}
defer cli.Stop()
peer, err := cli.ConnectDirectID(sock)
if err != nil {
t.Fatalf("dial %s: %v", sock, err)
}
b := NewBuilder(64)
o := b.StartObject(24)
o.SetBytes(0, []byte("ping"))
o.FinishAsRoot()
req, err := Parse(b.FinishWithFlags(uint16(op) << 8))
if err != nil {
t.Fatalf("build: %v", err)
}
resp, err := cli.Call(context.Background(), peer, req)
if err != nil {
t.Fatalf("Call over unix socket: %v", err)
}
if got := string(resp.Root().Bytes(0)); got != "pong" {
t.Fatalf("got %q want %q", got, "pong")
}
}
func TestNetwork(t *testing.T) {
for addr, want := range map[string]string{
"/run/tasks.sock": "unix",
"./tasks.sock": "unix",
"@tasks": "unix",
"127.0.0.1:9999": "tcp",
":9999": "tcp",
"tasks.hanzo.svc:9999": "tcp",
} {
if got := Network(addr); got != want {
t.Fatalf("Network(%q) = %q want %q", addr, got, want)
}
}
}