Commit d0dc6890 authored by Russ Cox's avatar Russ Cox

net/http: panic on duplicate registrations

Otherwise, the registration semantics are
init-order-dependent, which I was trying very hard
to avoid in the API.  This may break broken programs.

Fixes #2900.

R=golang-dev, r, bradfitz, dsymonds, balasanjay, kevlar
CC=golang-dev
https://golang.org/cl/5644051
parent 49110eaa
...@@ -1146,10 +1146,16 @@ The affected items are: ...@@ -1146,10 +1146,16 @@ The affected items are:
</ul> </ul>
<p> <p>
Also, the <code>Request.RawURL</code> field has been removed; it was a The <code>Request.RawURL</code> field has been removed; it was a
historical artifact. historical artifact.
</p> </p>
<p>
The <code>Handle</code> and <code>HandleFunc</code>
functions, and the similarly-named methods of <code>ServeMux</code>,
now panic if an attempt is made to register the same pattern twice.
</p>
<p> <p>
<em>Updating</em>: <em>Updating</em>:
Running <code>go fix</code> will update the few programs that are affected except for Running <code>go fix</code> will update the few programs that are affected except for
......
...@@ -1049,10 +1049,16 @@ The affected items are: ...@@ -1049,10 +1049,16 @@ The affected items are:
</ul> </ul>
<p> <p>
Also, the <code>Request.RawURL</code> field has been removed; it was a The <code>Request.RawURL</code> field has been removed; it was a
historical artifact. historical artifact.
</p> </p>
<p>
The <code>Handle</code> and <code>HandleFunc</code>
functions, and the similarly-named methods of <code>ServeMux</code>,
now panic if an attempt is made to register the same pattern twice.
</p>
<p> <p>
<em>Updating</em>: <em>Updating</em>:
Running <code>go fix</code> will update the few programs that are affected except for Running <code>go fix</code> will update the few programs that are affected except for
......
...@@ -833,11 +833,17 @@ func RedirectHandler(url string, code int) Handler { ...@@ -833,11 +833,17 @@ func RedirectHandler(url string, code int) Handler {
// redirecting any request containing . or .. elements to an // redirecting any request containing . or .. elements to an
// equivalent .- and ..-free URL. // equivalent .- and ..-free URL.
type ServeMux struct { type ServeMux struct {
m map[string]Handler mu sync.RWMutex
m map[string]muxEntry
}
type muxEntry struct {
explicit bool
h Handler
} }
// NewServeMux allocates and returns a new ServeMux. // NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return &ServeMux{make(map[string]Handler)} } func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} }
// DefaultServeMux is the default ServeMux used by Serve. // DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = NewServeMux() var DefaultServeMux = NewServeMux()
...@@ -883,12 +889,28 @@ func (mux *ServeMux) match(path string) Handler { ...@@ -883,12 +889,28 @@ func (mux *ServeMux) match(path string) Handler {
} }
if h == nil || len(k) > n { if h == nil || len(k) > n {
n = len(k) n = len(k)
h = v h = v.h
} }
} }
return h return h
} }
// handler returns the handler to use for the request r.
func (mux *ServeMux) handler(r *Request) Handler {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
h := mux.match(r.Host + r.URL.Path)
if h == nil {
h = mux.match(r.URL.Path)
}
if h == nil {
h = NotFoundHandler()
}
return h
}
// ServeHTTP dispatches the request to the handler whose // ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL. // pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
...@@ -898,30 +920,33 @@ func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { ...@@ -898,30 +920,33 @@ func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
w.WriteHeader(StatusMovedPermanently) w.WriteHeader(StatusMovedPermanently)
return return
} }
// Host-specific pattern takes precedence over generic ones mux.handler(r).ServeHTTP(w, r)
h := mux.match(r.Host + r.URL.Path)
if h == nil {
h = mux.match(r.URL.Path)
}
if h == nil {
h = NotFoundHandler()
}
h.ServeHTTP(w, r)
} }
// Handle registers the handler for the given pattern. // Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) { func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" { if pattern == "" {
panic("http: invalid pattern " + pattern) panic("http: invalid pattern " + pattern)
} }
if handler == nil {
panic("http: nil handler")
}
if mux.m[pattern].explicit {
panic("http: multiple registrations for " + pattern)
}
mux.m[pattern] = handler mux.m[pattern] = muxEntry{explicit: true, h: handler}
// Helpful behavior: // Helpful behavior:
// If pattern is /tree/, insert permanent redirect for /tree. // If pattern is /tree/, insert an implicit permanent redirect for /tree.
// It can be overridden by an explicit registration.
n := len(pattern) n := len(pattern)
if n > 0 && pattern[n-1] == '/' { if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
mux.m[pattern[0:n-1]] = RedirectHandler(pattern, StatusMovedPermanently) mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(pattern, StatusMovedPermanently)}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment