|
@@ -0,0 +1,127 @@
|
|
|
+package httpex
|
|
|
+
|
|
|
+import (
|
|
|
+ "net/http"
|
|
|
+ "path"
|
|
|
+ "sync"
|
|
|
+)
|
|
|
+
|
|
|
+type ServeMux struct {
|
|
|
+ mu sync.RWMutex
|
|
|
+ m map[string]muxEntry
|
|
|
+ hosts bool // whether any patterns contain hostnames
|
|
|
+}
|
|
|
+
|
|
|
+type muxEntry struct {
|
|
|
+ explicit bool
|
|
|
+ h http.Handler
|
|
|
+ pattern string
|
|
|
+}
|
|
|
+
|
|
|
+// NewServeMux allocates and returns a new ServeMux.
|
|
|
+func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} }
|
|
|
+
|
|
|
+// DefaultServeMux is the default ServeMux used by Serve.
|
|
|
+var DefaultServeMux = NewServeMux()
|
|
|
+
|
|
|
+// Does path match pattern?
|
|
|
+func pathMatch(pattern, path string) bool {
|
|
|
+ if len(pattern) == 0 {
|
|
|
+ // should not happen
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ n := len(pattern)
|
|
|
+ if pattern[n-1] != '/' {
|
|
|
+ return pattern == path
|
|
|
+ }
|
|
|
+ return len(path) >= n && path[0:n] == pattern
|
|
|
+}
|
|
|
+
|
|
|
+// Return the canonical path for p, eliminating . and .. elements.
|
|
|
+func cleanPath(p string) string {
|
|
|
+ if p == "" {
|
|
|
+ return "/"
|
|
|
+ }
|
|
|
+ if p[0] != '/' {
|
|
|
+ p = "/" + p
|
|
|
+ }
|
|
|
+ np := path.Clean(p)
|
|
|
+ // path.Clean removes trailing slash except for root;
|
|
|
+ // put the trailing slash back if necessary.
|
|
|
+ if p[len(p)-1] == '/' && np != "/" {
|
|
|
+ np += "/"
|
|
|
+ }
|
|
|
+ return np
|
|
|
+}
|
|
|
+
|
|
|
+// Find a handler on a handler map given a path string
|
|
|
+// Most-specific (longest) pattern wins
|
|
|
+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
|
|
|
+}
|
|
|
+
|
|
|
+// Handler returns the handler to use for the given request,
|
|
|
+// consulting r.Method, r.Host, and r.URL.Path. It always returns
|
|
|
+// a non-nil handler. If the path is not in its canonical form, the
|
|
|
+// handler will be an internally-generated handler that redirects
|
|
|
+// to the canonical path.
|
|
|
+//
|
|
|
+// 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.
|
|
|
+//
|
|
|
+// 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 *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()
|
|
|
+
|
|
|
+ // 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 = http.NotFoundHandler(), ""
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// ServeHTTP dispatches the request to the handler whose
|
|
|
+// pattern most closely matches the request URL.
|
|
|
+func (mux *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
+}
|
|
|
+
|
|
|
+func (mux *ServeMux) Handle(pattern string, handler http.Handler) {
|
|
|
+}
|
|
|
+
|
|
|
+// HandleFunc registers the handler function for the given pattern.
|
|
|
+func (mux *ServeMux) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
|
|
+}
|
|
|
+
|