Skip to content

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 ​

go
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:

go
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 ​

go
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
}
OptionDoes
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:

go
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:

go
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:

go
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.

MIT licensed