Bump Gorilla Mux to `v1.8.0`.
Signed-off-by: olegburov <oleg.burov@outlook.com>master
							parent
							
								
									2800ab0224
								
							
						
					
					
						commit
						545596ae2c
					
				
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							|  | @ -21,7 +21,7 @@ require ( | |||
| 	github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 | ||||
| 	github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 | ||||
| 	github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 | ||||
| 	github.com/gorilla/mux v1.7.2 | ||||
| 	github.com/gorilla/mux v1.8.0 | ||||
| 	github.com/inconshreveable/mousetrap v1.0.0 // indirect | ||||
| 	github.com/kr/pretty v0.1.0 // indirect | ||||
| 	github.com/marstr/guid v1.1.0 // indirect | ||||
|  |  | |||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							|  | @ -57,8 +57,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw | |||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo= | ||||
| github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= | ||||
| github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= | ||||
| github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | ||||
| github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= | ||||
| github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | ||||
| github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= | ||||
| github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= | ||||
| github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= | ||||
|  |  | |||
|  | @ -1,24 +0,0 @@ | |||
| language: go | ||||
| 
 | ||||
| 
 | ||||
| matrix: | ||||
|   include: | ||||
|     - go: 1.7.x | ||||
|     - go: 1.8.x | ||||
|     - go: 1.9.x | ||||
|     - go: 1.10.x | ||||
|     - go: 1.11.x | ||||
|     - go: 1.x | ||||
|       env: LATEST=true | ||||
|     - go: tip | ||||
|   allow_failures: | ||||
|     - go: tip | ||||
| 
 | ||||
| install: | ||||
|   - # Skip | ||||
| 
 | ||||
| script: | ||||
|   - go get -t -v ./... | ||||
|   - diff -u <(echo -n) <(gofmt -d .) | ||||
|   - if [[ "$LATEST" = true ]]; then go vet .; fi | ||||
|   - go test -v -race ./... | ||||
|  | @ -1,11 +0,0 @@ | |||
| **What version of Go are you running?** (Paste the output of `go version`) | ||||
| 
 | ||||
| 
 | ||||
| **What version of gorilla/mux are you at?** (Paste the output of `git rev-parse HEAD` inside `$GOPATH/src/github.com/gorilla/mux`) | ||||
| 
 | ||||
| 
 | ||||
| **Describe your problem** (and what you have tried so far) | ||||
| 
 | ||||
| 
 | ||||
| **Paste a minimal, runnable, reproduction of your issue below** (use backticks to format it) | ||||
| 
 | ||||
|  | @ -1,10 +1,10 @@ | |||
| # gorilla/mux | ||||
| 
 | ||||
| [](https://godoc.org/github.com/gorilla/mux) | ||||
| [](https://travis-ci.org/gorilla/mux) | ||||
| [](https://circleci.com/gh/gorilla/mux) | ||||
| [](https://sourcegraph.com/github.com/gorilla/mux?badge) | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| https://www.gorillatoolkit.org/pkg/mux | ||||
| 
 | ||||
|  | @ -25,10 +25,12 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv | |||
| * [Examples](#examples) | ||||
| * [Matching Routes](#matching-routes) | ||||
| * [Static Files](#static-files) | ||||
| * [Serving Single Page Applications](#serving-single-page-applications) (e.g. React, Vue, Ember.js, etc.) | ||||
| * [Registered URLs](#registered-urls) | ||||
| * [Walking Routes](#walking-routes) | ||||
| * [Graceful Shutdown](#graceful-shutdown) | ||||
| * [Middleware](#middleware) | ||||
| * [Handling CORS Requests](#handling-cors-requests) | ||||
| * [Testing Handlers](#testing-handlers) | ||||
| * [Full Example](#full-example) | ||||
| 
 | ||||
|  | @ -210,6 +212,93 @@ func main() { | |||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Serving Single Page Applications | ||||
| 
 | ||||
| Most of the time it makes sense to serve your SPA on a separate web server from your API, | ||||
| but sometimes it's desirable to serve them both from one place. It's possible to write a simple | ||||
| handler for serving your SPA (for use with React Router's [BrowserRouter](https://reacttraining.com/react-router/web/api/BrowserRouter) for example), and leverage | ||||
| mux's powerful routing for your API endpoints. | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/gorilla/mux" | ||||
| ) | ||||
| 
 | ||||
| // spaHandler implements the http.Handler interface, so we can use it | ||||
| // to respond to HTTP requests. The path to the static directory and | ||||
| // path to the index file within that static directory are used to | ||||
| // serve the SPA in the given static directory. | ||||
| type spaHandler struct { | ||||
| 	staticPath string | ||||
| 	indexPath  string | ||||
| } | ||||
| 
 | ||||
| // ServeHTTP inspects the URL path to locate a file within the static dir | ||||
| // on the SPA handler. If a file is found, it will be served. If not, the | ||||
| // file located at the index path on the SPA handler will be served. This | ||||
| // is suitable behavior for serving an SPA (single page application). | ||||
| func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
|     // get the absolute path to prevent directory traversal | ||||
| 	path, err := filepath.Abs(r.URL.Path) | ||||
| 	if err != nil { | ||||
|         // if we failed to get the absolute path respond with a 400 bad request | ||||
|         // and stop | ||||
| 		http.Error(w, err.Error(), http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|     // prepend the path with the path to the static directory | ||||
| 	path = filepath.Join(h.staticPath, path) | ||||
| 
 | ||||
|     // check whether a file exists at the given path | ||||
| 	_, err = os.Stat(path) | ||||
| 	if os.IsNotExist(err) { | ||||
| 		// file does not exist, serve index.html | ||||
| 		http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath)) | ||||
| 		return | ||||
| 	} else if err != nil { | ||||
|         // if we got an error (that wasn't that the file doesn't exist) stating the | ||||
|         // file, return a 500 internal server error and stop | ||||
| 		http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|     // otherwise, use http.FileServer to serve the static dir | ||||
| 	http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r) | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	router := mux.NewRouter() | ||||
| 
 | ||||
| 	router.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		// an example API handler | ||||
| 		json.NewEncoder(w).Encode(map[string]bool{"ok": true}) | ||||
| 	}) | ||||
| 
 | ||||
| 	spa := spaHandler{staticPath: "build", indexPath: "index.html"} | ||||
| 	router.PathPrefix("/").Handler(spa) | ||||
| 
 | ||||
| 	srv := &http.Server{ | ||||
| 		Handler: router, | ||||
| 		Addr:    "127.0.0.1:8000", | ||||
| 		// Good practice: enforce timeouts for servers you create! | ||||
| 		WriteTimeout: 15 * time.Second, | ||||
| 		ReadTimeout:  15 * time.Second, | ||||
| 	} | ||||
| 
 | ||||
| 	log.Fatal(srv.ListenAndServe()) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Registered URLs | ||||
| 
 | ||||
| Now let's see how to build registered URLs. | ||||
|  | @ -491,6 +580,73 @@ r.Use(amw.Middleware) | |||
| 
 | ||||
| Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares _should_ write to `ResponseWriter` if they _are_ going to terminate the request, and they _should not_ write to `ResponseWriter` if they _are not_ going to terminate it. | ||||
| 
 | ||||
| ### Handling CORS Requests | ||||
| 
 | ||||
| [CORSMethodMiddleware](https://godoc.org/github.com/gorilla/mux#CORSMethodMiddleware) intends to make it easier to strictly set the `Access-Control-Allow-Methods` response header. | ||||
| 
 | ||||
| * You will still need to use your own CORS handler to set the other CORS headers such as `Access-Control-Allow-Origin` | ||||
| * The middleware will set the `Access-Control-Allow-Methods` header to all the method matchers (e.g. `r.Methods(http.MethodGet, http.MethodPut, http.MethodOptions)` -> `Access-Control-Allow-Methods: GET,PUT,OPTIONS`) on a route | ||||
| * If you do not specify any methods, then: | ||||
| > _Important_: there must be an `OPTIONS` method matcher for the middleware to set the headers. | ||||
| 
 | ||||
| Here is an example of using `CORSMethodMiddleware` along with a custom `OPTIONS` handler to set all the required CORS headers: | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"github.com/gorilla/mux" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
|     r := mux.NewRouter() | ||||
| 
 | ||||
|     // IMPORTANT: you must specify an OPTIONS method matcher for the middleware to set CORS headers | ||||
|     r.HandleFunc("/foo", fooHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodOptions) | ||||
|     r.Use(mux.CORSMethodMiddleware(r)) | ||||
|      | ||||
|     http.ListenAndServe(":8080", r) | ||||
| } | ||||
| 
 | ||||
| func fooHandler(w http.ResponseWriter, r *http.Request) { | ||||
|     w.Header().Set("Access-Control-Allow-Origin", "*") | ||||
|     if r.Method == http.MethodOptions { | ||||
|         return | ||||
|     } | ||||
| 
 | ||||
|     w.Write([]byte("foo")) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| And an request to `/foo` using something like: | ||||
| 
 | ||||
| ```bash | ||||
| curl localhost:8080/foo -v | ||||
| ``` | ||||
| 
 | ||||
| Would look like: | ||||
| 
 | ||||
| ```bash | ||||
| *   Trying ::1... | ||||
| * TCP_NODELAY set | ||||
| * Connected to localhost (::1) port 8080 (#0) | ||||
| > GET /foo HTTP/1.1 | ||||
| > Host: localhost:8080 | ||||
| > User-Agent: curl/7.59.0 | ||||
| > Accept: */* | ||||
| >  | ||||
| < HTTP/1.1 200 OK | ||||
| < Access-Control-Allow-Methods: GET,PUT,PATCH,OPTIONS | ||||
| < Access-Control-Allow-Origin: * | ||||
| < Date: Fri, 28 Jun 2019 20:13:30 GMT | ||||
| < Content-Length: 3 | ||||
| < Content-Type: text/plain; charset=utf-8 | ||||
| <  | ||||
| * Connection #0 to host localhost left intact | ||||
| foo | ||||
| ``` | ||||
| 
 | ||||
| ### Testing Handlers | ||||
| 
 | ||||
| Testing handlers in a Go web application is straightforward, and _mux_ doesn't complicate this any further. Given two files: `endpoints.go` and `endpoints_test.go`, here's how we'd test an application using _mux_. | ||||
|  |  | |||
|  | @ -1,18 +0,0 @@ | |||
| package mux | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| func contextGet(r *http.Request, key interface{}) interface{} { | ||||
| 	return r.Context().Value(key) | ||||
| } | ||||
| 
 | ||||
| func contextSet(r *http.Request, key, val interface{}) *http.Request { | ||||
| 	if val == nil { | ||||
| 		return r | ||||
| 	} | ||||
| 
 | ||||
| 	return r.WithContext(context.WithValue(r.Context(), key, val)) | ||||
| } | ||||
|  | @ -295,7 +295,7 @@ A more complex authentication middleware, which maps session token to users, cou | |||
| 	r := mux.NewRouter() | ||||
| 	r.HandleFunc("/", handler) | ||||
| 
 | ||||
| 	amw := authenticationMiddleware{} | ||||
| 	amw := authenticationMiddleware{tokenUsers: make(map[string]string)} | ||||
| 	amw.Populate() | ||||
| 
 | ||||
| 	r.Use(amw.Middleware) | ||||
|  |  | |||
|  | @ -1 +1,3 @@ | |||
| module github.com/gorilla/mux | ||||
| 
 | ||||
| go 1.12 | ||||
|  |  | |||
|  | @ -32,37 +32,19 @@ 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.
 | ||||
| // CORSMethodMiddleware automatically sets the Access-Control-Allow-Methods response header
 | ||||
| // on requests for routes that have an OPTIONS method matcher to all the method matchers on
 | ||||
| // the route. Routes that do not explicitly handle OPTIONS requests will not be processed
 | ||||
| // by the middleware. See examples for usage.
 | ||||
| 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 | ||||
| 			}) | ||||
| 
 | ||||
| 			allMethods, err := getAllMethodsForRoute(r, req) | ||||
| 			if err == nil { | ||||
| 				w.Header().Set("Access-Control-Allow-Methods", strings.Join(append(allMethods, "OPTIONS"), ",")) | ||||
| 
 | ||||
| 				if req.Method == "OPTIONS" { | ||||
| 					return | ||||
| 				for _, v := range allMethods { | ||||
| 					if v == http.MethodOptions { | ||||
| 						w.Header().Set("Access-Control-Allow-Methods", strings.Join(allMethods, ",")) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
|  | @ -70,3 +52,23 @@ func CORSMethodMiddleware(r *Router) MiddlewareFunc { | |||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // getAllMethodsForRoute returns all the methods from method matchers matching a given
 | ||||
| // request.
 | ||||
| func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) { | ||||
| 	var allMethods []string | ||||
| 
 | ||||
| 	for _, route := range r.routes { | ||||
| 		var match RouteMatch | ||||
| 		if route.Match(req, &match) || match.MatchErr == ErrMethodMismatch { | ||||
| 			methods, err := route.GetMethods() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			allMethods = append(allMethods, methods...) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return allMethods, nil | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| package mux | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
|  | @ -58,8 +59,7 @@ type Router struct { | |||
| 
 | ||||
| 	// If true, do not clear the request context after handling the request.
 | ||||
| 	//
 | ||||
| 	// Deprecated: No effect when go1.7+ is used, since the context is stored
 | ||||
| 	// on the request itself.
 | ||||
| 	// Deprecated: No effect, since the context is stored on the request itself.
 | ||||
| 	KeepContext bool | ||||
| 
 | ||||
| 	// Slice of middlewares to be called after a match is found
 | ||||
|  | @ -111,10 +111,8 @@ func copyRouteConf(r routeConf) routeConf { | |||
| 		c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q)) | ||||
| 	} | ||||
| 
 | ||||
| 	c.matchers = make([]matcher, 0, len(r.matchers)) | ||||
| 	for _, m := range r.matchers { | ||||
| 		c.matchers = append(c.matchers, m) | ||||
| 	} | ||||
| 	c.matchers = make([]matcher, len(r.matchers)) | ||||
| 	copy(c.matchers, r.matchers) | ||||
| 
 | ||||
| 	return c | ||||
| } | ||||
|  | @ -197,8 +195,8 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { | |||
| 	var handler http.Handler | ||||
| 	if r.Match(req, &match) { | ||||
| 		handler = match.Handler | ||||
| 		req = setVars(req, match.Vars) | ||||
| 		req = setCurrentRoute(req, match.Route) | ||||
| 		req = requestWithVars(req, match.Vars) | ||||
| 		req = requestWithRoute(req, match.Route) | ||||
| 	} | ||||
| 
 | ||||
| 	if handler == nil && match.MatchErr == ErrMethodMismatch { | ||||
|  | @ -428,7 +426,7 @@ const ( | |||
| 
 | ||||
| // Vars returns the route variables for the current request, if any.
 | ||||
| func Vars(r *http.Request) map[string]string { | ||||
| 	if rv := contextGet(r, varsKey); rv != nil { | ||||
| 	if rv := r.Context().Value(varsKey); rv != nil { | ||||
| 		return rv.(map[string]string) | ||||
| 	} | ||||
| 	return nil | ||||
|  | @ -437,21 +435,22 @@ func Vars(r *http.Request) map[string]string { | |||
| // CurrentRoute returns the matched route for the current request, if any.
 | ||||
| // This only works when called inside the handler of the matched route
 | ||||
| // because the matched route is stored in the request context which is cleared
 | ||||
| // after the handler returns, unless the KeepContext option is set on the
 | ||||
| // Router.
 | ||||
| // after the handler returns.
 | ||||
| func CurrentRoute(r *http.Request) *Route { | ||||
| 	if rv := contextGet(r, routeKey); rv != nil { | ||||
| 	if rv := r.Context().Value(routeKey); rv != nil { | ||||
| 		return rv.(*Route) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func setVars(r *http.Request, val interface{}) *http.Request { | ||||
| 	return contextSet(r, varsKey, val) | ||||
| func requestWithVars(r *http.Request, vars map[string]string) *http.Request { | ||||
| 	ctx := context.WithValue(r.Context(), varsKey, vars) | ||||
| 	return r.WithContext(ctx) | ||||
| } | ||||
| 
 | ||||
| func setCurrentRoute(r *http.Request, val interface{}) *http.Request { | ||||
| 	return contextSet(r, routeKey, val) | ||||
| func requestWithRoute(r *http.Request, route *Route) *http.Request { | ||||
| 	ctx := context.WithValue(r.Context(), routeKey, route) | ||||
| 	return r.WithContext(ctx) | ||||
| } | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
|  |  | |||
|  | @ -181,7 +181,8 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { | |||
| 			} | ||||
| 		} | ||||
| 		return r.regexp.MatchString(host) | ||||
| 	} else { | ||||
| 	} | ||||
| 
 | ||||
| 	if r.regexpType == regexpTypeQuery { | ||||
| 		return r.matchQueryString(req) | ||||
| 	} | ||||
|  | @ -190,12 +191,11 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { | |||
| 		path = req.URL.EscapedPath() | ||||
| 	} | ||||
| 	return r.regexp.MatchString(path) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // url builds a URL part using the given values.
 | ||||
| func (r *routeRegexp) url(values map[string]string) (string, error) { | ||||
| 	urlValues := make([]interface{}, len(r.varsN)) | ||||
| 	urlValues := make([]interface{}, len(r.varsN), len(r.varsN)) | ||||
| 	for k, v := range r.varsN { | ||||
| 		value, ok := values[v] | ||||
| 		if !ok { | ||||
|  | @ -230,14 +230,51 @@ func (r *routeRegexp) getURLQuery(req *http.Request) string { | |||
| 		return "" | ||||
| 	} | ||||
| 	templateKey := strings.SplitN(r.template, "=", 2)[0] | ||||
| 	for key, vals := range req.URL.Query() { | ||||
| 		if key == templateKey && len(vals) > 0 { | ||||
| 			return key + "=" + vals[0] | ||||
| 		} | ||||
| 	val, ok := findFirstQueryKey(req.URL.RawQuery, templateKey) | ||||
| 	if ok { | ||||
| 		return templateKey + "=" + val | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| // findFirstQueryKey returns the same result as (*url.URL).Query()[key][0].
 | ||||
| // If key was not found, empty string and false is returned.
 | ||||
| func findFirstQueryKey(rawQuery, key string) (value string, ok bool) { | ||||
| 	query := []byte(rawQuery) | ||||
| 	for len(query) > 0 { | ||||
| 		foundKey := query | ||||
| 		if i := bytes.IndexAny(foundKey, "&;"); i >= 0 { | ||||
| 			foundKey, query = foundKey[:i], foundKey[i+1:] | ||||
| 		} else { | ||||
| 			query = query[:0] | ||||
| 		} | ||||
| 		if len(foundKey) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		var value []byte | ||||
| 		if i := bytes.IndexByte(foundKey, '='); i >= 0 { | ||||
| 			foundKey, value = foundKey[:i], foundKey[i+1:] | ||||
| 		} | ||||
| 		if len(foundKey) < len(key) { | ||||
| 			// Cannot possibly be key.
 | ||||
| 			continue | ||||
| 		} | ||||
| 		keyString, err := url.QueryUnescape(string(foundKey)) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		if keyString != key { | ||||
| 			continue | ||||
| 		} | ||||
| 		valueString, err := url.QueryUnescape(string(value)) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		return valueString, true | ||||
| 	} | ||||
| 	return "", false | ||||
| } | ||||
| 
 | ||||
| func (r *routeRegexp) matchQueryString(req *http.Request) bool { | ||||
| 	return r.regexp.MatchString(r.getURLQuery(req)) | ||||
| } | ||||
|  | @ -288,6 +325,12 @@ func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { | |||
| 	// Store host variables.
 | ||||
| 	if v.host != nil { | ||||
| 		host := getHost(req) | ||||
| 		if v.host.wildcardHostPort { | ||||
| 			// Don't be strict on the port match
 | ||||
| 			if i := strings.Index(host, ":"); i != -1 { | ||||
| 				host = host[:i] | ||||
| 			} | ||||
| 		} | ||||
| 		matches := v.host.regexp.FindStringSubmatchIndex(host) | ||||
| 		if len(matches) > 0 { | ||||
| 			extractVars(host, matches, v.host.varsN, m.Vars) | ||||
|  |  | |||
|  | @ -74,7 +74,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { | |||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if match.MatchErr == ErrMethodMismatch { | ||||
| 	if match.MatchErr == ErrMethodMismatch && r.handler != nil { | ||||
| 		// We found a route which matches request method, clear MatchErr
 | ||||
| 		match.MatchErr = nil | ||||
| 		// Then override the mis-matched handler
 | ||||
|  | @ -412,11 +412,30 @@ func (r *Route) Queries(pairs ...string) *Route { | |||
| type schemeMatcher []string | ||||
| 
 | ||||
| func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { | ||||
| 	return matchInArray(m, r.URL.Scheme) | ||||
| 	scheme := r.URL.Scheme | ||||
| 	// https://golang.org/pkg/net/http/#Request
 | ||||
| 	// "For [most] server requests, fields other than Path and RawQuery will be
 | ||||
| 	// empty."
 | ||||
| 	// Since we're an http muxer, the scheme is either going to be http or https
 | ||||
| 	// though, so we can just set it based on the tls termination state.
 | ||||
| 	if scheme == "" { | ||||
| 		if r.TLS == nil { | ||||
| 			scheme = "http" | ||||
| 		} else { | ||||
| 			scheme = "https" | ||||
| 		} | ||||
| 	} | ||||
| 	return matchInArray(m, scheme) | ||||
| } | ||||
| 
 | ||||
| // Schemes adds a matcher for URL schemes.
 | ||||
| // It accepts a sequence of schemes to be matched, e.g.: "http", "https".
 | ||||
| // If the request's URL has a scheme set, it will be matched against.
 | ||||
| // Generally, the URL scheme will only be set if a previous handler set it,
 | ||||
| // such as the ProxyHeaders handler from gorilla/handlers.
 | ||||
| // If unset, the scheme will be determined based on the request's TLS
 | ||||
| // termination state.
 | ||||
| // The first argument to Schemes will be used when constructing a route URL.
 | ||||
| func (r *Route) Schemes(schemes ...string) *Route { | ||||
| 	for k, v := range schemes { | ||||
| 		schemes[k] = strings.ToLower(v) | ||||
|  | @ -493,8 +512,8 @@ func (r *Route) Subrouter() *Router { | |||
| // This also works for host variables:
 | ||||
| //
 | ||||
| //     r := mux.NewRouter()
 | ||||
| //     r.Host("{subdomain}.domain.com").
 | ||||
| //       HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
 | ||||
| //     r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
 | ||||
| //       Host("{subdomain}.domain.com").
 | ||||
| //       Name("article")
 | ||||
| //
 | ||||
| //     // url.String() will be "http://news.domain.com/articles/technology/42"
 | ||||
|  | @ -502,6 +521,13 @@ func (r *Route) Subrouter() *Router { | |||
| //                                      "category", "technology",
 | ||||
| //                                      "id", "42")
 | ||||
| //
 | ||||
| // The scheme of the resulting url will be the first argument that was passed to Schemes:
 | ||||
| //
 | ||||
| //     // url.String() will be "https://example.com"
 | ||||
| //     r := mux.NewRouter()
 | ||||
| //     url, err := r.Host("example.com")
 | ||||
| //                  .Schemes("https", "http").URL()
 | ||||
| //
 | ||||
| // All variables defined in the route are required, and their values must
 | ||||
| // conform to the corresponding patterns.
 | ||||
| func (r *Route) URL(pairs ...string) (*url.URL, error) { | ||||
|  | @ -635,7 +661,7 @@ func (r *Route) GetQueriesRegexp() ([]string, error) { | |||
| 	if r.regexp.queries == nil { | ||||
| 		return nil, errors.New("mux: route doesn't have queries") | ||||
| 	} | ||||
| 	var queries []string | ||||
| 	queries := make([]string, 0, len(r.regexp.queries)) | ||||
| 	for _, query := range r.regexp.queries { | ||||
| 		queries = append(queries, query.regexp.String()) | ||||
| 	} | ||||
|  | @ -654,7 +680,7 @@ func (r *Route) GetQueriesTemplates() ([]string, error) { | |||
| 	if r.regexp.queries == nil { | ||||
| 		return nil, errors.New("mux: route doesn't have queries") | ||||
| 	} | ||||
| 	var queries []string | ||||
| 	queries := make([]string, 0, len(r.regexp.queries)) | ||||
| 	for _, query := range r.regexp.queries { | ||||
| 		queries = append(queries, query.template) | ||||
| 	} | ||||
|  |  | |||
|  | @ -15,5 +15,5 @@ import "net/http" | |||
| // can be set by making a route that captures the required variables,
 | ||||
| // starting a server and sending the request to that server.
 | ||||
| func SetURLVars(r *http.Request, val map[string]string) *http.Request { | ||||
| 	return setVars(r, val) | ||||
| 	return requestWithVars(r, val) | ||||
| } | ||||
|  |  | |||
|  | @ -86,7 +86,7 @@ github.com/garyburd/redigo/redis | |||
| github.com/golang/protobuf/proto | ||||
| # github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 | ||||
| github.com/gorilla/handlers | ||||
| # github.com/gorilla/mux v1.7.2 | ||||
| # github.com/gorilla/mux v1.8.0 | ||||
| github.com/gorilla/mux | ||||
| # github.com/inconshreveable/mousetrap v1.0.0 | ||||
| github.com/inconshreveable/mousetrap | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue