HTTP over ZAP
Drop-in net/http replacement carried on the ZAP wire.
HTTP over ZAP
zap-proto/http is a drop-in replacement for Go's net/http that speaks the ZAP wire format. Existing http.Handler code compiles unchanged; the difference is zero-copy buffers end-to-end, mutual KEM auth at the transport, and 5-10× lower p99 vs. net/http on the same hardware (see benchmarks).
Install
go get github.com/zap-proto/httpServer
package main
import (
"io"
"log"
zhttp "github.com/zap-proto/http"
)
func main() {
h := zhttp.NewServeMux()
h.HandleFunc("/hello", func(w zhttp.ResponseWriter, r *zhttp.Request) {
w.WriteHeader(200)
io.WriteString(w, "hello, "+r.URL.Path)
})
log.Fatal(zhttp.ListenAndServe(":9000", h))
}The signatures are intentionally identical to net/http — the only difference is the package import. ResponseWriter and Request are interfaces that wrap zero-copy ZAP buffers.
Client
client := zhttp.DefaultClient
resp, err := client.Get("zap://api.example.com/hello")
if err != nil { log.Fatal(err) }
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))Middleware
Standard pattern works:
func auth(next zhttp.Handler) zhttp.Handler {
return zhttp.HandlerFunc(func(w zhttp.ResponseWriter, r *zhttp.Request) {
if r.PQIdentity() == nil {
zhttp.Error(w, "unauthorized", 401)
return
}
next.ServeHTTP(w, r)
})
}
h.Handle("/private", auth(handler))r.PQIdentity() returns the peer's KEM-bound identity — the auth happens at the ZAP transport, your handler doesn't issue/verify tokens.
Bridge to net/http
Wrap an existing http.Handler:
import (
nhttp "net/http"
zhttp "github.com/zap-proto/http"
)
var existing nhttp.Handler = setupRouter() // your existing routes
log.Fatal(zhttp.ListenAndServe(":9000", zhttp.FromNetHTTP(existing)))Performance
See zap-proto/bench for the reproducible harness. The current harness (bench-results.txt) measures HTTP/1.1+JSON against ZAP-binary across tiny/small/medium/large workloads at concurrency=32:
- Wire bytes: roughly parity (0.91-0.99× across sizes — ZAP wins at small payloads, ties at large)
- Throughput: HTTP currently wins by ~10% at small/medium workloads — the ZAP-HTTP path has more allocations than tight
net/http - Allocations: HTTP currently allocates ~30% less per request
This is honest: ZAP-HTTP today is not faster than tuned net/http. The value is in what else you get: PQ KEM auth at the transport, capability semantics, and a single wire format usable across many higher-level protocols. Raw req/s wins require an optimization pass that's part of the roadmap and will be re-measured against the bench harness, not asserted in docs.
Related
- zap-proto/http — Go reference implementation
- zap-proto/bench — performance harness
- transports — wire-level framing detail