Skip to content

Returning A Singleton Middleware Handler Breaks Routing #995

@mmmmmrob

Description

@mmmmmrob

Middlewares register with chi by providing a func(next http.Handler) http.Handler function that returns a http.Handler that is expected to call the given next argument to pass the request on.

When using .Group(…), chi calls the given middleware function multiple times to create copies of the middleware stack for the group, the godoc says "Group creates a new inline-Mux with a copy of middleware stack."

Returning the same instance of http.Handler for each call results in all routing being broken and all requests being passed to the last group that was registered. The request is passed to a handler within the final group even if the path does not match.

Expected behaviour: The same instance of http.Handler can be returned and the routing will be correct.

Reproduction:

Run the following server and request http://localhost/, http://localhost/foo, and http://localhost/bar.

The expected responses would be Hello World!, Hello Foo!, and Hello Bar! but all responses are Hello Bar!.

package main

import (
	"fmt"
	"net/http"

	"github.com/go-chi/chi/v5"
)

func hello(text string) func(http.ResponseWriter, *http.Request) {
	output := fmt.Sprintf("Hello %s\n", text)
	return func(w http.ResponseWriter, r *http.Request) {
		fmt.Println(output)
		w.Header().Add("Content-Type", "text/plain")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(output))
	}
}

type middleware struct {
	next http.Handler
}

func (m *middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	m.next.ServeHTTP(w, r)
}

var singleton = middleware{}

func Middleware(next http.Handler) http.Handler {
	singleton.next = next
	return &singleton
}

func main() {

	roothandler := chi.NewRouter()

	roothandler.Group(func(topgroup chi.Router) {
		topgroup.Use(Middleware)

		topgroup.Get("/", hello("World!"))

		topgroup.Group(func(foogroup chi.Router) {
			foogroup.Get("/foo", hello("Foo!"))
		})

		topgroup.Group(func(bargroup chi.Router) {
			bargroup.Get("/bar", hello("Bar!"))
		})
	})

	server := http.Server{
		Handler: roothandler,
	}

	server.ListenAndServe()
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions