Files
zeekayandHanzo Dev ecf51b7bf3 a net/http handler runs over ZAP unchanged
The package is called http and now looks like it. Request, Response, Header,
Handler, HandlerFunc, ResponseWriter and ServeMux are ALIASES of net/http's own
types, so an existing handler satisfies them exactly and middleware, httptest
and mux compose for free. A declared struct with matching fields would be a
different type — every caller converting, every middleware failing — and drop-in
would be a claim rather than a fact.

ListenAndServe takes the standard Handler, and there is one of it. A second one
taking fasthttp.RequestHandler used to sit in server.go, so there were two ways
to start a server differing only in which handler shape you happened to hold. A
caller with a fasthttp handler composes &Server{Addr, Handler} — one line that
says what it is.

The cost is named rather than hidden: net/http's Request and ResponseWriter
allocate where fasthttp's do not, and this package guards a zero-alloc encode.
So the fast path is untouched — Server.Handler is still a fasthttp handler and
nothing in this file is in its way. Adapt is exported so the bridge is visible at
the call site, which is where a net/http Request gets materialised.

The example is an ordinary net/http handler now. Nothing in it knows about ZAP,
which is the whole demonstration.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-10 17:02:58 -07:00

53 lines
1.5 KiB
Go

// Minimal zap-http example: server + client in one binary.
//
// go run ./examples/hello server :9999
// go run ./examples/hello client :9999
//
// The server speaks ZAP-HTTP on :9999 with a fasthttp handler. The client
// dials, issues GET /hello, and prints the response body.
package main
import (
"fmt"
"os"
"github.com/valyala/fasthttp"
http "github.com/zap-proto/http"
)
func main() {
if len(os.Args) < 3 {
fmt.Fprintf(os.Stderr, "usage: %s [server|client] addr\n", os.Args[0])
os.Exit(2)
}
role, addr := os.Args[1], os.Args[2]
switch role {
case "server":
// An ordinary net/http handler. Nothing here knows about ZAP — that is
// the point: the import changed and the code did not.
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "hello from %s — you said %s %s\n", addr, r.Method, r.URL.Path)
})
fmt.Printf("zap-http server listening on %s\n", addr)
if err := http.ListenAndServe(addr, handler); err != nil {
fmt.Fprintln(os.Stderr, "server:", err)
os.Exit(1)
}
case "client":
resp, err := http.Get(addr, "/hello")
if err != nil {
fmt.Fprintln(os.Stderr, "client:", err)
os.Exit(1)
}
defer fasthttp.ReleaseResponse(resp)
fmt.Printf("← %d %s\n", resp.StatusCode(), fasthttp.StatusMessage(resp.StatusCode()))
os.Stdout.Write(resp.Body())
default:
fmt.Fprintln(os.Stderr, "role must be 'server' or 'client'")
os.Exit(2)
}
}