-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
This will fall into the Go 2 category bucket, because as proposed it is a breaking behavioral change for Go 1.
Proposal
Instead of panicking on invalid http.Handler
registrations, (*http.ServeMux).Handle
should return errors. This is germane in when the user attempts to register multiple http.Handler
against the same path as demonstrated with http.DefaultServeMux
:
http.Handle("/path", h)
http.Handle("/path", h)
// Output:
panic: http: multiple registrations for /path
Instead, I propose (*http.ServeMux).Handle
gets a new method signature similar to this:
func (*http.ServeMux) Handle(string, Handler) error { ... }
In the case above, a sentinel or structured error value could be returned by package http
to enable callers to handle the error:
package http
var ErrMultipleRegistrations = errors.New("http: more than one handler registered against the path")
// Or:
type MultipleRegistrationError struct {
Path string
}
func (err *MultipleRegistrationError) Error() string { ... }
Motivation
Why is this panic worth handling at all? Shouldn't the user know about all registrations and be able to prevent multiple?
I believe the latter question is the first to start with. Based on our team's experience in offering a server framework used internally by several thousand customer teams, we got to see first-hand how the multiple registration behavior played in a gigantic monorepo, especially when combined with code that registered default debugging handlers using http.Handle
in func init
bodies. It played out terribly. This was because there were two registration pathways:
- through the server framework directly, which could catch duplicates of thing registered through it
- things registered directly by other infrastructure providers in
func init
onhttp.DefaultServeMux
usinghttp.Handle
and similar
We could only control no. 1. Auditing no. 2 was a boil-the-ocean affair, particularly because there was no wisdom nor enforcement of avoiding mutating package state in registrations. Attempting to convert no. 2 to support per-http.ServeMux
registration was a good compromise, but we could never catch up with the codebase.
In effect, this means that it's infeasible in large codebases to ensure that there is central control over who is registering what. We tried with a per-server http.ServeMux
that falls back to http.DefaultServeMux
for paths it doesn't recognize, but the result and behavior is not pretty. It's also error-prone.
Returning an error sentinel or structured error value would enable intermediate frameworks and middleware to more gracefully handle such cases, which is a huge win for developer experience.
Other Thoughts and Speculation
An API for unregistering existing handlers might be interesting to consider. A server framework could de-register a duplicate. Or as a twist on this, last-one-wins semantic for registration? I haven't thought about these super closely, but I can say the panic
should go.
Perhaps a structured error return (as suggested above) could return information about the registrations: what package, what line, identifier type information, or interface value of the pre-existing http.Handler
.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status