Remove support for matching schemes
This commit is contained in:
parent
5a07b49ef5
commit
a49b0cdaac
@ -46,11 +46,11 @@ func main() {
|
||||
|
||||
handler := &gmi.ServeMux{}
|
||||
handler.HandleFunc("/", welcome)
|
||||
handler.HandleFunc("/login/", login)
|
||||
handler.HandleFunc("/login/password/", loginPassword)
|
||||
handler.HandleFunc("/profile/", profile)
|
||||
handler.HandleFunc("/admin/", admin)
|
||||
handler.HandleFunc("/logout/", logout)
|
||||
handler.HandleFunc("/login", login)
|
||||
handler.HandleFunc("/login/password", loginPassword)
|
||||
handler.HandleFunc("/profile", profile)
|
||||
handler.HandleFunc("/admin", admin)
|
||||
handler.HandleFunc("/logout", logout)
|
||||
|
||||
server := &gmi.Server{
|
||||
Certificate: cert,
|
||||
|
@ -1,46 +0,0 @@
|
||||
package gmi
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestServeMuxEntryOrder(t *testing.T) {
|
||||
expected := []string{
|
||||
"https://example.com/longpath",
|
||||
"https://example.com/path",
|
||||
"https://example.com",
|
||||
"http://example.com/longpath",
|
||||
"http://example.com/path",
|
||||
"http://example.com",
|
||||
"example.com/longpath",
|
||||
"example.com/path",
|
||||
"example.com",
|
||||
"/longpath",
|
||||
"/path",
|
||||
"/",
|
||||
}
|
||||
|
||||
// Shuffle input
|
||||
a := make([]string, len(expected))
|
||||
copy(a, expected)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.Shuffle(len(a), func(i, j int) { a[i], a[j] = a[j], a[i] })
|
||||
|
||||
mux := &ServeMux{}
|
||||
for _, s := range a {
|
||||
mux.Handle(s, NotFoundHandler())
|
||||
var es string
|
||||
for i := range mux.es {
|
||||
es += mux.es[i].u.String() + " "
|
||||
}
|
||||
t.Logf(es)
|
||||
}
|
||||
for i, e := range mux.es {
|
||||
s := e.u.String()
|
||||
if s != expected[i] {
|
||||
t.Errorf("wrong order of mux entries: expected %s, got %s", expected[i], s)
|
||||
}
|
||||
}
|
||||
}
|
96
server.go
96
server.go
@ -454,15 +454,15 @@ func isSlashRune(r rune) bool { return r == '/' || r == '\\' }
|
||||
// header, stripping the port number and redirecting any request containing . or
|
||||
// .. elements or repeated slashes to an equivalent, cleaner URL.
|
||||
type ServeMux struct {
|
||||
mu sync.RWMutex
|
||||
m map[string]muxEntry
|
||||
es []muxEntry // slice of entries sorted from longest to shortest.
|
||||
mu sync.RWMutex
|
||||
m map[string]muxEntry
|
||||
es []muxEntry // slice of entries sorted from longest to shortest.
|
||||
hosts bool // whether any patterns contain hostnames
|
||||
}
|
||||
|
||||
type muxEntry struct {
|
||||
h Handler
|
||||
pattern string
|
||||
u *url.URL
|
||||
}
|
||||
|
||||
// NewServeMux allocates and returns a new ServeMux.
|
||||
@ -505,19 +505,17 @@ func stripHostPort(h string) string {
|
||||
|
||||
// Find a handler on a handler map given a path string.
|
||||
// Most-specific (longest) pattern wins.
|
||||
func (mux *ServeMux) match(url *url.URL) (h Handler, pattern string) {
|
||||
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
|
||||
// Check for exact match first.
|
||||
v, ok := mux.m[url.String()]
|
||||
v, ok := mux.m[path]
|
||||
if ok {
|
||||
return v.h, v.pattern
|
||||
}
|
||||
|
||||
// Check for longest valid match. mux.es contains all patterns
|
||||
// Check for longest valid match. mux.es contains all patterns
|
||||
// that end in / sorted from longest to shortest.
|
||||
for _, e := range mux.es {
|
||||
if (e.u.Scheme == "" || url.Scheme == e.u.Scheme) &&
|
||||
(e.u.Host == "" || url.Host == e.u.Host) &&
|
||||
strings.HasPrefix(url.Path, e.u.Path) {
|
||||
if strings.HasPrefix(path, e.pattern) {
|
||||
return e.h, e.pattern
|
||||
}
|
||||
}
|
||||
@ -572,6 +570,8 @@ func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool {
|
||||
// to the canonical path. If the host contains a port, it is ignored
|
||||
// when matching handlers.
|
||||
//
|
||||
// The path and host are used unchanged for CONNECT requests.
|
||||
//
|
||||
// Handler also returns the registered pattern that matches the
|
||||
// request or, in the case of internally-generated redirects,
|
||||
// the pattern that will match after following the redirect.
|
||||
@ -579,34 +579,40 @@ func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool {
|
||||
// If there is no registered handler that applies to the request,
|
||||
// Handler returns a ``page not found'' handler and an empty pattern.
|
||||
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
|
||||
// All requests have any port stripped and path cleaned
|
||||
// All other requests have any port stripped and path cleaned
|
||||
// before passing to mux.handler.
|
||||
url := *r.URL
|
||||
url.Host = stripHostPort(r.Host)
|
||||
url.Path = cleanPath(r.URL.Path)
|
||||
host := stripHostPort(r.Host)
|
||||
path := cleanPath(r.URL.Path)
|
||||
|
||||
// If the given path is /tree and its handler is not registered,
|
||||
// redirect for /tree/.
|
||||
if u, ok := mux.redirectToPathSlash(url.Host, url.Path, r.URL); ok {
|
||||
return PermanentRedirectHandler(u.String()), u.Path
|
||||
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
|
||||
return RedirectHandler(u.String()), u.Path
|
||||
}
|
||||
|
||||
if url.Path != r.URL.Path {
|
||||
_, pattern = mux.handler(&url)
|
||||
red := *r.URL
|
||||
red.Path = url.Path
|
||||
return PermanentRedirectHandler(red.String()), pattern
|
||||
if path != r.URL.Path {
|
||||
_, pattern = mux.handler(host, path)
|
||||
url := *r.URL
|
||||
url.Path = path
|
||||
return RedirectHandler(url.String()), pattern
|
||||
}
|
||||
|
||||
return mux.handler(&url)
|
||||
return mux.handler(host, r.URL.Path)
|
||||
}
|
||||
|
||||
// handler is the main implementation of Handler.
|
||||
func (mux *ServeMux) handler(url *url.URL) (h Handler, pattern string) {
|
||||
// The path is known to be in canonical form, except for CONNECT methods.
|
||||
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
|
||||
mux.mu.RLock()
|
||||
defer mux.mu.RUnlock()
|
||||
|
||||
h, pattern = mux.match(url)
|
||||
// Host-specific pattern takes precedence over generic ones
|
||||
if mux.hosts {
|
||||
h, pattern = mux.match(host + path)
|
||||
}
|
||||
if h == nil {
|
||||
h, pattern = mux.match(path)
|
||||
}
|
||||
if h == nil {
|
||||
h, pattern = NotFoundHandler(), ""
|
||||
}
|
||||
@ -639,45 +645,21 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) {
|
||||
if mux.m == nil {
|
||||
mux.m = make(map[string]muxEntry)
|
||||
}
|
||||
url, err := url.Parse(pattern)
|
||||
if err != nil {
|
||||
panic("gmi: invalid pattern")
|
||||
}
|
||||
e := muxEntry{h: handler, pattern: pattern, u: url}
|
||||
e := muxEntry{h: handler, pattern: pattern}
|
||||
mux.m[pattern] = e
|
||||
// if pattern[len(pattern)-1] == '/' {
|
||||
mux.es = appendSorted(mux.es, e)
|
||||
// }
|
||||
if pattern[len(pattern)-1] == '/' {
|
||||
mux.es = appendSorted(mux.es, e)
|
||||
}
|
||||
|
||||
if pattern[0] != '/' {
|
||||
mux.hosts = true
|
||||
}
|
||||
}
|
||||
|
||||
func appendSorted(es []muxEntry, e muxEntry) []muxEntry {
|
||||
n := len(es)
|
||||
i := sort.Search(n, func(i int) bool {
|
||||
// Sort entries by length.
|
||||
// - Entries with a scheme take preference over entries without.
|
||||
// - Entries with a host take preference over entries without.
|
||||
// - Longer paths take preference over shorter paths.
|
||||
if e.u.Scheme != "" {
|
||||
if es[i].u.Scheme == "" {
|
||||
return true
|
||||
}
|
||||
if es[i].u.Scheme != e.u.Scheme {
|
||||
return len(es[i].u.Scheme) < len(e.u.Scheme)
|
||||
}
|
||||
} else if es[i].u.Scheme != "" {
|
||||
return false
|
||||
}
|
||||
if e.u.Host != "" {
|
||||
if es[i].u.Host == "" {
|
||||
return true
|
||||
}
|
||||
if es[i].u.Host != e.u.Host {
|
||||
return len(es[i].u.Scheme) < len(e.u.Scheme)
|
||||
}
|
||||
} else if es[i].u.Host != "" {
|
||||
return false
|
||||
}
|
||||
return len(es[i].u.Path) < len(e.u.Path)
|
||||
return len(es[i].pattern) < len(e.pattern)
|
||||
})
|
||||
if i == n {
|
||||
return append(es, e)
|
||||
|
Loading…
Reference in New Issue
Block a user