Merge pull request #2568 from thaJeztah/bump_mux

vendor: gorilla/mux v1.7.4
This commit is contained in:
Silvin Lubecki 2020-06-25 15:31:35 +02:00 committed by GitHub
commit 691d3b7a7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 201 additions and 72 deletions

View File

@ -30,7 +30,7 @@ github.com/google/go-cmp 3af367b6b30c263d47e8895973ed
github.com/google/gofuzz 24818f796faf91cd76ec7bddd72458fbced7a6c1 github.com/google/gofuzz 24818f796faf91cd76ec7bddd72458fbced7a6c1
github.com/google/shlex e7afc7fbc51079733e9468cdfd1efcd7d196cd1d github.com/google/shlex e7afc7fbc51079733e9468cdfd1efcd7d196cd1d
github.com/googleapis/gnostic 7c663266750e7d82587642f65e60bc4083f1f84e # v0.2.0 github.com/googleapis/gnostic 7c663266750e7d82587642f65e60bc4083f1f84e # v0.2.0
github.com/gorilla/mux 00bdffe0f3c77e27d2cf6f5c70232a2d3e4d9c15 # v1.7.3 github.com/gorilla/mux 75dcda0896e109a2a22c9315bca3bb21b87b2ba5 # v1.7.4
github.com/grpc-ecosystem/grpc-gateway 1a03ca3bad1e1ebadaedd3abb76bc58d4ac8143b github.com/grpc-ecosystem/grpc-gateway 1a03ca3bad1e1ebadaedd3abb76bc58d4ac8143b
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746 github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
github.com/hashicorp/golang-lru 7f827b33c0f158ec5dfbba01bb0b14a4541fd81d # v0.5.3 github.com/hashicorp/golang-lru 7f827b33c0f158ec5dfbba01bb0b14a4541fd81d # v0.5.3

View File

@ -1,11 +1,10 @@
# gorilla/mux # gorilla/mux
[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) [![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux) [![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux)
[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge)
![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png) ![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png)
https://www.gorillatoolkit.org/pkg/mux https://www.gorillatoolkit.org/pkg/mux
@ -26,6 +25,7 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv
* [Examples](#examples) * [Examples](#examples)
* [Matching Routes](#matching-routes) * [Matching Routes](#matching-routes)
* [Static Files](#static-files) * [Static Files](#static-files)
* [Serving Single Page Applications](#serving-single-page-applications) (e.g. React, Vue, Ember.js, etc.)
* [Registered URLs](#registered-urls) * [Registered URLs](#registered-urls)
* [Walking Routes](#walking-routes) * [Walking Routes](#walking-routes)
* [Graceful Shutdown](#graceful-shutdown) * [Graceful Shutdown](#graceful-shutdown)
@ -212,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 ### Registered URLs
Now let's see how to build registered URLs. Now let's see how to build registered URLs.

View File

@ -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))
}

View File

@ -1 +1,3 @@
module github.com/gorilla/mux module github.com/gorilla/mux
go 1.12

View File

@ -58,22 +58,17 @@ func CORSMethodMiddleware(r *Router) MiddlewareFunc {
func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) { func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) {
var allMethods []string var allMethods []string
err := r.Walk(func(route *Route, _ *Router, _ []*Route) error { for _, route := range r.routes {
for _, m := range route.matchers { var match RouteMatch
if _, ok := m.(*routeRegexp); ok { if route.Match(req, &match) || match.MatchErr == ErrMethodMismatch {
if m.Match(req, &RouteMatch{}) { methods, err := route.GetMethods()
methods, err := route.GetMethods() if err != nil {
if err != nil { return nil, err
return err
}
allMethods = append(allMethods, methods...)
}
break
} }
}
return nil
})
return allMethods, err allMethods = append(allMethods, methods...)
}
}
return allMethods, nil
} }

28
vendor/github.com/gorilla/mux/mux.go generated vendored
View File

@ -5,6 +5,7 @@
package mux package mux
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@ -58,8 +59,7 @@ type Router struct {
// If true, do not clear the request context after handling the request. // 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 // Deprecated: No effect, since the context is stored on the request itself.
// on the request itself.
KeepContext bool KeepContext bool
// Slice of middlewares to be called after a match is found // 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.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q))
} }
c.matchers = make([]matcher, 0, len(r.matchers)) c.matchers = make([]matcher, len(r.matchers))
for _, m := range r.matchers { copy(c.matchers, r.matchers)
c.matchers = append(c.matchers, m)
}
return c return c
} }
@ -197,8 +195,8 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var handler http.Handler var handler http.Handler
if r.Match(req, &match) { if r.Match(req, &match) {
handler = match.Handler handler = match.Handler
req = setVars(req, match.Vars) req = requestWithVars(req, match.Vars)
req = setCurrentRoute(req, match.Route) req = requestWithRoute(req, match.Route)
} }
if handler == nil && match.MatchErr == ErrMethodMismatch { if handler == nil && match.MatchErr == ErrMethodMismatch {
@ -428,7 +426,7 @@ const (
// Vars returns the route variables for the current request, if any. // Vars returns the route variables for the current request, if any.
func Vars(r *http.Request) map[string]string { 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 rv.(map[string]string)
} }
return nil return nil
@ -440,18 +438,20 @@ func Vars(r *http.Request) map[string]string {
// after the handler returns, unless the KeepContext option is set on the // after the handler returns, unless the KeepContext option is set on the
// Router. // Router.
func CurrentRoute(r *http.Request) *Route { 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 rv.(*Route)
} }
return nil return nil
} }
func setVars(r *http.Request, val interface{}) *http.Request { func requestWithVars(r *http.Request, vars map[string]string) *http.Request {
return contextSet(r, varsKey, val) ctx := context.WithValue(r.Context(), varsKey, vars)
return r.WithContext(ctx)
} }
func setCurrentRoute(r *http.Request, val interface{}) *http.Request { func requestWithRoute(r *http.Request, route *Route) *http.Request {
return contextSet(r, routeKey, val) ctx := context.WithValue(r.Context(), routeKey, route)
return r.WithContext(ctx)
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -181,21 +181,21 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
} }
} }
return r.regexp.MatchString(host) return r.regexp.MatchString(host)
} else {
if r.regexpType == regexpTypeQuery {
return r.matchQueryString(req)
}
path := req.URL.Path
if r.options.useEncodedPath {
path = req.URL.EscapedPath()
}
return r.regexp.MatchString(path)
} }
if r.regexpType == regexpTypeQuery {
return r.matchQueryString(req)
}
path := req.URL.Path
if r.options.useEncodedPath {
path = req.URL.EscapedPath()
}
return r.regexp.MatchString(path)
} }
// url builds a URL part using the given values. // url builds a URL part using the given values.
func (r *routeRegexp) url(values map[string]string) (string, error) { 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 { for k, v := range r.varsN {
value, ok := values[v] value, ok := values[v]
if !ok { if !ok {
@ -230,14 +230,51 @@ func (r *routeRegexp) getURLQuery(req *http.Request) string {
return "" return ""
} }
templateKey := strings.SplitN(r.template, "=", 2)[0] templateKey := strings.SplitN(r.template, "=", 2)[0]
for key, vals := range req.URL.Query() { val, ok := findFirstQueryKey(req.URL.RawQuery, templateKey)
if key == templateKey && len(vals) > 0 { if ok {
return key + "=" + vals[0] return templateKey + "=" + val
}
} }
return "" 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 { func (r *routeRegexp) matchQueryString(req *http.Request) bool {
return r.regexp.MatchString(r.getURLQuery(req)) return r.regexp.MatchString(r.getURLQuery(req))
} }

View File

@ -74,7 +74,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
return false return false
} }
if match.MatchErr == ErrMethodMismatch { if match.MatchErr == ErrMethodMismatch && r.handler != nil {
// We found a route which matches request method, clear MatchErr // We found a route which matches request method, clear MatchErr
match.MatchErr = nil match.MatchErr = nil
// Then override the mis-matched handler // Then override the mis-matched handler
@ -412,11 +412,30 @@ func (r *Route) Queries(pairs ...string) *Route {
type schemeMatcher []string type schemeMatcher []string
func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { 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. // Schemes adds a matcher for URL schemes.
// It accepts a sequence of schemes to be matched, e.g.: "http", "https". // 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 { func (r *Route) Schemes(schemes ...string) *Route {
for k, v := range schemes { for k, v := range schemes {
schemes[k] = strings.ToLower(v) schemes[k] = strings.ToLower(v)
@ -493,8 +512,8 @@ func (r *Route) Subrouter() *Router {
// This also works for host variables: // This also works for host variables:
// //
// r := mux.NewRouter() // r := mux.NewRouter()
// r.Host("{subdomain}.domain.com"). // r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). // Host("{subdomain}.domain.com").
// Name("article") // Name("article")
// //
// // url.String() will be "http://news.domain.com/articles/technology/42" // // url.String() will be "http://news.domain.com/articles/technology/42"
@ -502,6 +521,13 @@ func (r *Route) Subrouter() *Router {
// "category", "technology", // "category", "technology",
// "id", "42") // "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 // All variables defined in the route are required, and their values must
// conform to the corresponding patterns. // conform to the corresponding patterns.
func (r *Route) URL(pairs ...string) (*url.URL, error) { func (r *Route) URL(pairs ...string) (*url.URL, error) {
@ -635,7 +661,7 @@ func (r *Route) GetQueriesRegexp() ([]string, error) {
if r.regexp.queries == nil { if r.regexp.queries == nil {
return nil, errors.New("mux: route doesn't have queries") 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 { for _, query := range r.regexp.queries {
queries = append(queries, query.regexp.String()) queries = append(queries, query.regexp.String())
} }
@ -654,7 +680,7 @@ func (r *Route) GetQueriesTemplates() ([]string, error) {
if r.regexp.queries == nil { if r.regexp.queries == nil {
return nil, errors.New("mux: route doesn't have queries") 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 { for _, query := range r.regexp.queries {
queries = append(queries, query.template) queries = append(queries, query.template)
} }

View File

@ -15,5 +15,5 @@ import "net/http"
// can be set by making a route that captures the required variables, // can be set by making a route that captures the required variables,
// starting a server and sending the request to that server. // starting a server and sending the request to that server.
func SetURLVars(r *http.Request, val map[string]string) *http.Request { func SetURLVars(r *http.Request, val map[string]string) *http.Request {
return setVars(r, val) return requestWithVars(r, val)
} }