net/http, chi and gorilla/mux
Package chanxhttp mounts the engine on anything built on net/http: the standard ServeMux, chi, gorilla/mux, and frameworks that take a plain http.Handler.
ServeMux
func NetHTTP(engine *chanx.Engine) *http.ServeMux {
mux := http.NewServeMux()
chanxhttp.Mount(mux, engine) // registers "GET /ws/chat/{room}"
chanxhttp.MountDocs(mux, engine, "/asyncapi", asyncapi.Info{Title: "Chat"})
return mux
}Mount registers GET /ws/chat/{room}, and the consumer reads {room} through r.PathValue. A full server with graceful shutdown:
func Serve(ctx context.Context, engine *chanx.Engine, mux *http.ServeMux) error {
srv := &http.Server{Addr: ":8000", Handler: mux}
go func() {
<-ctx.Done()
shutdown, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_ = chanxhttp.Shutdown(shutdown, srv, engine) // the server, then every socket with 1001
}()
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
return err
}
return nil
}Options
func NetHTTPOptions(engine *chanx.Engine) *http.ServeMux {
mux := http.NewServeMux()
chanxhttp.Mount(mux, engine,
chanxhttp.Prefix("/api"), // serves /api/ws/chat/{room}
chanxhttp.AcceptOptions(&coderws.Options{OriginPatterns: []string{"app.example.com"}}),
chanxhttp.ClientIP(func(r *http.Request) string { return r.Header.Get("X-Real-IP") }),
)
return mux
}| Option | Does |
|---|---|
chanxhttp.Prefix("/api") | prepends a path to every route |
chanxhttp.AcceptOptions(&coderws.Options{...}) | origins, subprotocols, compression |
chanxhttp.Upgrade(fn) | another WebSocket library, see below |
chanxhttp.ClientIP(fn) | how the client address is read; RemoteAddr by default |
chanxhttp.NoMethod() | registers /ws/... instead of GET /ws/... |
Origins
coder/websocket accepts only an Origin matching the request's host by default. A frontend served from another origin needs OriginPatterns.
Behind a proxy, ClientIP should use the same trusted-proxy logic as the rest of your app: X-Forwarded-For is whatever the client wrote unless your proxy overwrites it.
chi
chi takes the same path syntax, without a method in the pattern:
r := chi.NewRouter()
r.Use(authMiddleware)
chanxhttp.Mount(r, engine, chanxhttp.NoMethod())or route by route, r.Get("/ws/chat/{room}", chanxhttp.Handler(engine, route)).
gorilla/mux and prefix mounts
Dispatcher serves every route from one handler, matching the URL itself:
r := mux.NewRouter()
r.PathPrefix("/ws/").Handler(chanxhttp.Dispatcher(engine))Unknown paths get 404. It fits any router that can hand a prefix to a handler.
Other frameworks
go-zero, Kratos, Huma and Encore raw endpoints register a plain http.Handler; give them chanxhttp.Handler(engine, route). Middleware values reach the consumer through the request context: context.WithValue in the middleware, chanx.UserFrom(key) or r.Value(key) in chanx.
gorilla/websocket
The default upgrade uses coder/websocket. To use gorilla's instead, for its options or because the team already does:
func Gorilla(engine *chanx.Engine) *http.ServeMux {
upgrader := &gorillaws.Upgrader{CheckOrigin: func(*http.Request) bool { return true }}
mux := http.NewServeMux()
chanxhttp.Mount(mux, engine, chanxhttp.Upgrade(func(w http.ResponseWriter, r *http.Request) (transport.Conn, error) {
return gorillaws.Accept(w, r, upgrader)
}))
return mux
}Next: gin.