From 99b50e6caf9f9bd4665df527905efe00657cfb2b Mon Sep 17 00:00:00 2001 From: adnano Date: Fri, 25 Sep 2020 11:00:18 -0400 Subject: [PATCH] Sort ServeMux entries by length --- examples/server/server.go | 22 +++++++++++------- gemini.go | 47 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/examples/server/server.go b/examples/server/server.go index 466d024..3d1145d 100644 --- a/examples/server/server.go +++ b/examples/server/server.go @@ -30,14 +30,20 @@ func main() { } mux := &gemini.ServeMux{} - mux.HandleFunc("/cert", func(rw *gemini.ResponseWriter, req *gemini.Request) { - rw.WriteHeader(gemini.StatusClientCertificateRequired, "Certificate required") - }) - mux.HandleFunc("/", func(rw *gemini.ResponseWriter, req *gemini.Request) { - log.Printf("Request from %s for %s with certificates %v", req.RemoteAddr.String(), req.URL.String(), req.TLS.PeerCertificates) - rw.WriteHeader(gemini.StatusSuccess, "text/gemini") - rw.Write([]byte("You requested " + req.URL.String())) - }) + // mux.HandleFunc("/", func(rw *gemini.ResponseWriter, req *gemini.Request) { + // log.Printf("Request from %s for %s with certificates %v", req.RemoteAddr.String(), req.URL.String(), req.TLS.PeerCertificates) + // rw.WriteHeader(gemini.StatusSuccess, "text/gemini") + // rw.Write([]byte("You requested " + req.URL.String())) + // }) + // mux.HandleFunc("/cert", func(rw *gemini.ResponseWriter, req *gemini.Request) { + // rw.WriteHeader(gemini.StatusClientCertificateRequired, "Certificate required") + // }) + + mux.HandleFunc("https://example.com/path", nil) + mux.HandleFunc("http://example.com/path", nil) + mux.HandleFunc("example.com/path", nil) + mux.HandleFunc("/path", nil) + mux.HandleFunc("/longpath", nil) server := gemini.Server{ TLSConfig: config, diff --git a/gemini.go b/gemini.go index f94c01e..514fc36 100644 --- a/gemini.go +++ b/gemini.go @@ -9,6 +9,7 @@ import ( "log" "net" "net/url" + "sort" "strconv" "strings" "time" @@ -437,12 +438,14 @@ func (m *ServeMux) Handle(pattern string, handler Handler) { if err != nil { panic(err) } - m.entries = append(m.entries, muxEntry{ + e := muxEntry{ url.Scheme, url.Host, url.Path, handler, - }) + } + m.entries = appendSorted(m.entries, e) + log.Print(m.entries) } // HandleFunc registers a HandlerFunc for the given pattern. @@ -461,6 +464,46 @@ func (m *ServeMux) Serve(rw *ResponseWriter, req *Request) { h.Serve(rw, req) } +// appendSorted appends the entry e in the proper place in entries. +func appendSorted(es []muxEntry, e muxEntry) []muxEntry { + n := len(es) + // sort by length + 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. + // + // Long version: + // if es[i].scheme != "" { + // if e.scheme == "" { + // return false + // } + // return len(es[i].scheme) < len(e.scheme) + // } + // if es[i].host != "" { + // if e.host == "" { + // return false + // } + // return len(es[i].host) < len(e.host) + // } + // return len(es[i].path) < len(e.path) + + // Condensed version: + return (es[i].scheme == "" || (e.scheme != "" && len(es[i].scheme) < len(e.scheme))) && + (es[i].host == "" || (e.host != "" && len(es[i].host) < len(e.host))) && + len(es[i].path) < len(e.path) + }) + if i == n { + return append(es, e) + } + // we now know that i points at where we want to insert + es = append(es, muxEntry{}) // try to grow the slice in place, any entry works. + copy(es[i+1:], es[i:]) // Move shorter entries down + es[i] = e + return es +} + // A wrapper around a bare function that implements Handler. type HandlerFunc func(*ResponseWriter, *Request)