Run(args ...any) errorRun creates an *http.Server (with the app itself as handler) and blocks until the server stops.
Pass a string as the listen address:
app.Run(":8080") // default: 0.0.0.0:8080
app.Run("127.0.0.1:3000") // localhost only
app.Run(":0") // random available portWith no arguments, the default address :8080 is used:
app.Run()Pass a net.Listener — useful for custom network types or externally controlled lifecycles:
ln, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
app.Run(ln)Also works for testing or Unix sockets:
ln, _ := net.Listen("unix", "/tmp/app.sock")
app.Run(ln)- On a normal stop,
http.ErrServerClosedis swallowed andRunreturnsnil. - Other errors (port in use, listen failure) are returned as-is.
Routes and middleware must be fully registered before Run. Once serving, the route trees are read-only; each request acquires an RLock, so concurrent serving is safe.
When no route matches, the built-in defaultNotFound responds:
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
404 page not found
app.Shutdown(ctx) gracefully stops the server (waiting for active connections to finish or ctx to expire). Call it from another goroutine while Run is blocking:
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
<-sig
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = app.Shutdown(ctx)
}()
if err := app.Run(":8080"); err != nil { log.Fatal(err) }Run returns nil after a graceful shutdown (http.ErrServerClosed is swallowed).
Run applies safe server defaults: read/write timeouts 60s, read-header timeout 10s, MaxHeaderBytes 1 MiB. Override with New(WithConfig(...)) (only non-zero fields take effect):
app := httpsrv.New(httpsrv.WithConfig(httpsrv.Config{
Addr: ":3000",
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
ReadHeaderTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}))
WriteTimeoutcaps a single response's duration; set it to0for long-lived connections / SSE / large downloads.
middleware/compress's New() (mirroring gofiber v3's compress.New) gzip/brotli-compresses by Accept-Encoding (brotli preferred), sets Content-Encoding/Vary, and drops Content-Length:
import "github.com/hooto/httpsrv/v2/middleware/compress"
app.Use(compress.New()) // default level
app.Use(compress.New(compress.Config{ // or pick a level
Level: compress.LevelBestCompression,
}))When a Handler returns a non-nil error, the default writes 500. Customize with WithErrorHandler:
app := httpsrv.New(httpsrv.WithErrorHandler(func(c httpsrv.Ctx, err error) {
_ = c.Status(500).JSON(map[string]string{"error": err.Error()})
}))- Route matching is a read-only operation, naturally concurrency-safe.
- Avoid registering routes / middleware during
Run; do all registration before startup. - Unit tests pass under
-race:go test -race ./....