|
@@ -0,0 +1,127 @@
|
|
|
+package httpex
|
|
|
+
|
|
|
+import (
|
|
|
+ "net/http"
|
|
|
+ "path"
|
|
|
+ "sync"
|
|
|
+)
|
|
|
+
|
|
|
+type ServeMux struct {
|
|
|
+ mu sync.RWMutex
|
|
|
+ m map[string]muxEntry
|
|
|
+ hosts bool
|
|
|
+}
|
|
|
+
|
|
|
+type muxEntry struct {
|
|
|
+ explicit bool
|
|
|
+ h http.Handler
|
|
|
+ pattern string
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} }
|
|
|
+
|
|
|
+
|
|
|
+var DefaultServeMux = NewServeMux()
|
|
|
+
|
|
|
+
|
|
|
+func pathMatch(pattern, path string) bool {
|
|
|
+ if len(pattern) == 0 {
|
|
|
+
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ n := len(pattern)
|
|
|
+ if pattern[n-1] != '/' {
|
|
|
+ return pattern == path
|
|
|
+ }
|
|
|
+ return len(path) >= n && path[0:n] == pattern
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func cleanPath(p string) string {
|
|
|
+ if p == "" {
|
|
|
+ return "/"
|
|
|
+ }
|
|
|
+ if p[0] != '/' {
|
|
|
+ p = "/" + p
|
|
|
+ }
|
|
|
+ np := path.Clean(p)
|
|
|
+
|
|
|
+
|
|
|
+ if p[len(p)-1] == '/' && np != "/" {
|
|
|
+ np += "/"
|
|
|
+ }
|
|
|
+ return np
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (mux *ServeMux) match(path string) (h http.Handler, pattern string) {
|
|
|
+ var n = 0
|
|
|
+ for k, v := range mux.m {
|
|
|
+ if !pathMatch(k, path) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if h == nil || len(k) > n {
|
|
|
+ n = len(k)
|
|
|
+ h = v.h
|
|
|
+ pattern = v.pattern
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (mux *ServeMux) Handler(r *http.Request) (http.Handler, pattern string) {
|
|
|
+ if r.Method != "CONNECT" {
|
|
|
+ if p := cleanPath(r.URL.Path); p != r.URL.Path {
|
|
|
+ _, pattern = mux.handler(r.Host, p)
|
|
|
+ url := *r.URL
|
|
|
+ url.Path = p
|
|
|
+ return http.RedirectHandler(url.String(), http.StatusMovedPermanently), pattern
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return mux.handler(r.Host, r.URL.Path)
|
|
|
+}
|
|
|
+
|
|
|
+func (mux *ServeMux) handler(host, path string) (h http.Handler, pattern string) {
|
|
|
+ mux.mu.RLock()
|
|
|
+ defer mux.mu.RUnlock()
|
|
|
+
|
|
|
+
|
|
|
+ if mux.hosts {
|
|
|
+ h, pattern = mux.match(host + path)
|
|
|
+ }
|
|
|
+ if h == nil {
|
|
|
+ h, pattern = mux.match(path)
|
|
|
+ }
|
|
|
+ if h == nil {
|
|
|
+ h, pattern = http.NotFoundHandler(), ""
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (mux *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
+}
|
|
|
+
|
|
|
+func (mux *ServeMux) Handle(pattern string, handler http.Handler) {
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (mux *ServeMux) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
|
|
+}
|
|
|
+
|