Repository: gorilla/mux
Pull request #371 (2018-05-09 05:32:39)

Initialize user map


Patch:
diff --git a/README.md b/README.md
index 8f55ddc..e424397 100644
--- a/README.md
+++ b/README.md
@@ -601,8 +601,11 @@ func TestMetricsHandler(t *testing.T) {
         }
 
         rr := httptest.NewRecorder()
-        handler := http.HandlerFunc(MetricsHandler)
-        handler.ServeHTTP(rr, req)
+	
+	// Need to create a router that we can pass the request through so that the vars will be added to the context
+	router := mux.NewRouter()
+        router.HandleFunc("/metrics/{type}", MetricsHandler)
+        router.ServeHTTP(rr, req)
 
         // In this case, our MetricsHandler returns a non-200 response
         // for a route variable it doesn't know about.
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..cfc8ede
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,1 @@
+module github.com/gorilla/mux
diff --git a/middleware.go b/middleware.go
index cf6cfc3..ceb812c 100644
--- a/middleware.go
+++ b/middleware.go
@@ -1,6 +1,9 @@
 package mux
 
-import "net/http"
+import (
+	"net/http"
+	"strings"
+)
 
 // MiddlewareFunc is a function which receives an http.Handler and returns another http.Handler.
 // Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed
@@ -12,7 +15,7 @@ type middleware interface {
 	Middleware(handler http.Handler) http.Handler
 }
 
-// Middleware also implements the middleware interface.
+// Middleware allows MiddlewareFunc to implement the middleware interface.
 func (mw MiddlewareFunc) Middleware(handler http.Handler) http.Handler {
 	return mw(handler)
 }
@@ -28,3 +31,42 @@ func (r *Router) Use(mwf ...MiddlewareFunc) {
 func (r *Router) useInterface(mw middleware) {
 	r.middlewares = append(r.middlewares, mw)
 }
+
+// CORSMethodMiddleware sets the Access-Control-Allow-Methods response header
+// on a request, by matching routes based only on paths. It also handles
+// OPTIONS requests, by settings Access-Control-Allow-Methods, and then
+// returning without calling the next http handler.
+func CORSMethodMiddleware(r *Router) MiddlewareFunc {
+	return func(next http.Handler) http.Handler {
+		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+			var allMethods []string
+
+			err := r.Walk(func(route *Route, _ *Router, _ []*Route) error {
+				for _, m := range route.matchers {
+					if _, ok := m.(*routeRegexp); ok {
+						if m.Match(req, &RouteMatch{}) {
+							methods, err := route.GetMethods()
+							if err != nil {
+								return err
+							}
+
+							allMethods = append(allMethods, methods...)
+						}
+						break
+					}
+				}
+				return nil
+			})
+
+			if err == nil {
+				w.Header().Set("Access-Control-Allow-Methods", strings.Join(append(allMethods, "OPTIONS"), ","))
+
+				if req.Method == "OPTIONS" {
+					return
+				}
+			}
+
+			next.ServeHTTP(w, req)
+		})
+	}
+}
diff --git a/mux.go b/mux.go
index 5b39694..4bbafa5 100644
--- a/mux.go
+++ b/mux.go
@@ -13,7 +13,7 @@ import (
 )
 
 var (
-	// ErrMethodMismatch is returned when the error in the request does not match
+	// ErrMethodMismatch is returned when the method in the request does not match
 	// the method defined against the route.
 	ErrMethodMismatch = errors.New("method is not allowed")
 	// ErrNotFound is returned when no route match is found.
diff --git a/route.go b/route.go
index cc37ad6..a591d73 100644
--- a/route.go
+++ b/route.go
@@ -43,8 +43,8 @@ type Route struct {
 	buildVarsFunc BuildVarsFunc
 }
 
-// SkipClean bypasses cleaning the path, which includes removing duplicate
-// slashes and URL encoding.
+// SkipClean reports whether path cleaning is enabled for this route via
+// Router.SkipClean.
 func (r *Route) SkipClean() bool {
 	return r.skipClean
 }


Test patch:
diff --git a/example_authentication_middleware_test.go b/example_authentication_middleware_test.go
index 67df321..6f2ea86 100644
--- a/example_authentication_middleware_test.go
+++ b/example_authentication_middleware_test.go
@@ -40,7 +40,7 @@ func Example_authenticationMiddleware() {
 	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 		// Do something here
 	})
-	amw := authenticationMiddleware{}
+	amw := authenticationMiddleware{make(map[string]string)}
 	amw.Populate()
 	r.Use(amw.Middleware)
 }
diff --git a/middleware_test.go b/middleware_test.go
index 93947e8..acf4e16 100644
--- a/middleware_test.go
+++ b/middleware_test.go
@@ -3,6 +3,7 @@ package mux
 import (
 	"bytes"
 	"net/http"
+	"net/http/httptest"
 	"testing"
 )
 
@@ -334,3 +335,43 @@ func TestMiddlewareMethodMismatchSubrouter(t *testing.T) {
 		t.Fatal("Middleware was called for a method mismatch")
 	}
 }
+
+func TestCORSMethodMiddleware(t *testing.T) {
+	router := NewRouter()
+
+	cases := []struct {
+		path                   string
+		response               string
+		method                 string
+		testURL                string
+		expectedAllowedMethods string
+	}{
+		{"/g/{o}", "a", "POST", "/g/asdf", "POST,PUT,GET,OPTIONS"},
+		{"/g/{o}", "b", "PUT", "/g/bla", "POST,PUT,GET,OPTIONS"},
+		{"/g/{o}", "c", "GET", "/g/orilla", "POST,PUT,GET,OPTIONS"},
+		{"/g", "d", "POST", "/g", "POST,OPTIONS"},
+	}
+
+	for _, tt := range cases {
+		router.HandleFunc(tt.path, stringHandler(tt.response)).Methods(tt.method)
+	}
+
+	router.Use(CORSMethodMiddleware(router))
+
+	for _, tt := range cases {
+		rr := httptest.NewRecorder()
+		req := newRequest(tt.method, tt.testURL)
+
+		router.ServeHTTP(rr, req)
+
+		if rr.Body.String() != tt.response {
+			t.Errorf("Expected body '%s', found '%s'", tt.response, rr.Body.String())
+		}
+
+		allowedMethods := rr.HeaderMap.Get("Access-Control-Allow-Methods")
+
+		if allowedMethods != tt.expectedAllowedMethods {
+			t.Errorf("Expected Access-Control-Allow-Methods '%s', found '%s'", tt.expectedAllowedMethods, allowedMethods)
+		}
+	}
+}
diff --git a/mux_test.go b/mux_test.go
index 4591344..af21329 100644
--- a/mux_test.go
+++ b/mux_test.go
@@ -2315,6 +2315,14 @@ func stringMapEqual(m1, m2 map[string]string) bool {
 	return true
 }
 
+// stringHandler returns a handler func that writes a message 's' to the
+// http.ResponseWriter.
+func stringHandler(s string) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte(s))
+	}
+}
+
 // newRequest is a helper function to create a new request with a method and url.
 // The request returned is a 'server' request as opposed to a 'client' one through
 // simulated write onto the wire and read off of the wire.