Update gorilla/mux dependency to v1.8.1

This commit is contained in:
Cameron Moore 2024-03-29 13:05:30 -05:00
parent 81a17df5e0
commit 102e129280
No known key found for this signature in database
GPG key ID: AF96E12468D7553E
17 changed files with 367 additions and 149 deletions

2
go.mod
View file

@ -9,7 +9,7 @@ require (
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
github.com/go-chi/chi/v5 v5.0.12 github.com/go-chi/chi/v5 v5.0.12
github.com/gofrs/uuid/v5 v5.0.0 github.com/gofrs/uuid/v5 v5.0.0
github.com/gorilla/mux v1.7.3 github.com/gorilla/mux v1.8.1
github.com/kr/pretty v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect

2
go.sum
View file

@ -12,6 +12,8 @@ github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=

20
vendor/github.com/gorilla/mux/.editorconfig generated vendored Normal file
View file

@ -0,0 +1,20 @@
; https://editorconfig.org/
root = true
[*]
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
indent_style = tab
indent_size = 4
[*.md]
indent_size = 4
trim_trailing_whitespace = false
eclint_indent_style = unset

1
vendor/github.com/gorilla/mux/.gitignore generated vendored Normal file
View file

@ -0,0 +1 @@
coverage.coverprofile

View file

@ -1,8 +0,0 @@
# This is the official list of gorilla/mux authors for copyright purposes.
#
# Please keep the list sorted.
Google LLC (https://opensource.google.com/)
Kamil Kisielk <kamil@kamilkisiel.net>
Matt Silverlock <matt@eatsleeprepeat.net>
Rodrigo Moraes (https://github.com/moraes)

View file

@ -1,4 +1,4 @@
Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. Copyright (c) 2023 The Gorilla Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are

34
vendor/github.com/gorilla/mux/Makefile generated vendored Normal file
View file

@ -0,0 +1,34 @@
GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '')
GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest
GO_SEC=$(shell which gosec 2> /dev/null || echo '')
GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest
GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '')
GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest
.PHONY: golangci-lint
golangci-lint:
$(if $(GO_LINT), ,go install $(GO_LINT_URI))
@echo "##### Running golangci-lint"
golangci-lint run -v
.PHONY: gosec
gosec:
$(if $(GO_SEC), ,go install $(GO_SEC_URI))
@echo "##### Running gosec"
gosec ./...
.PHONY: govulncheck
govulncheck:
$(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI))
@echo "##### Running govulncheck"
govulncheck ./...
.PHONY: verify
verify: golangci-lint gosec govulncheck
.PHONY: test
test:
@echo "##### Running tests"
go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./...

View file

@ -1,13 +1,12 @@
# gorilla/mux # gorilla/mux
[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) ![testing](https://github.com/gorilla/mux/actions/workflows/test.yml/badge.svg)
[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux) [![codecov](https://codecov.io/github/gorilla/mux/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/mux)
[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux) [![godoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/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)
https://www.gorillatoolkit.org/pkg/mux ![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5)
Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
their respective handler. their respective handler.
@ -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,86 @@ 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) {
// Join internally call path.Clean to prevent directory traversal
path := filepath.Join(h.staticPath, r.URL.Path)
// check whether a file exists or is a directory at the given path
fi, err := os.Stat(path)
if os.IsNotExist(err) || fi.IsDir() {
// file does not exist or path is a directory, serve index.html
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
return
}
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 file
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.
@ -288,6 +368,19 @@ url, err := r.Get("article").URL("subdomain", "news",
"id", "42") "id", "42")
``` ```
To find all the required variables for a given route when calling `URL()`, the method `GetVarNames()` is available:
```go
r := mux.NewRouter()
r.Host("{domain}").
Path("/{group}/{item_id}").
Queries("some_data1", "{some_data1}").
Queries("some_data2", "{some_data2}").
Name("article")
// Will print [domain group item_id some_data1 some_data2] <nil>
fmt.Println(r.Get("article").GetVarNames())
```
### Walking Routes ### Walking Routes
The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example, The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example,
@ -485,7 +578,7 @@ func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/", handler) r.HandleFunc("/", handler)
amw := authenticationMiddleware{} amw := authenticationMiddleware{tokenUsers: make(map[string]string)}
amw.Populate() amw.Populate()
r.Use(amw.Middleware) r.Use(amw.Middleware)
@ -671,7 +764,8 @@ func TestMetricsHandler(t *testing.T) {
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
// Need to create a router that we can pass the request through so that the vars will be added to the context // To add the vars to the context,
// we need to create a router through which we can pass the request.
router := mux.NewRouter() router := mux.NewRouter()
router.HandleFunc("/metrics/{type}", MetricsHandler) router.HandleFunc("/metrics/{type}", MetricsHandler)
router.ServeHTTP(rr, req) router.ServeHTTP(rr, req)

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

11
vendor/github.com/gorilla/mux/doc.go generated vendored
View file

@ -10,17 +10,17 @@ http.ServeMux, mux.Router matches incoming requests against a list of
registered routes and calls a handler for the route that matches the URL registered routes and calls a handler for the route that matches the URL
or other conditions. The main features are: or other conditions. The main features are:
* Requests can be matched based on URL host, path, path prefix, schemes, - Requests can be matched based on URL host, path, path prefix, schemes,
header and query values, HTTP methods or using custom matchers. header and query values, HTTP methods or using custom matchers.
* URL hosts, paths and query values can have variables with an optional - URL hosts, paths and query values can have variables with an optional
regular expression. regular expression.
* Registered URLs can be built, or "reversed", which helps maintaining - Registered URLs can be built, or "reversed", which helps maintaining
references to resources. references to resources.
* Routes can be used as subrouters: nested routes are only tested if the - Routes can be used as subrouters: nested routes are only tested if the
parent route matches. This is useful to define groups of routes that parent route matches. This is useful to define groups of routes that
share common conditions like a host, a path prefix or other repeated share common conditions like a host, a path prefix or other repeated
attributes. As a bonus, this optimizes request matching. attributes. As a bonus, this optimizes request matching.
* It implements the http.Handler interface so it is compatible with the - It implements the http.Handler interface so it is compatible with the
standard http.ServeMux. standard http.ServeMux.
Let's start registering a couple of URL paths and handlers: Let's start registering a couple of URL paths and handlers:
@ -301,6 +301,5 @@ A more complex authentication middleware, which maps session token to users, cou
r.Use(amw.Middleware) 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. 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.
*/ */
package mux package mux

View file

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

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 err return nil, err
} }
allMethods = append(allMethods, methods...) allMethods = append(allMethods, methods...)
} }
break
} }
}
return nil
})
return allMethods, err return allMethods, nil
} }

33
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"
@ -45,9 +46,11 @@ func NewRouter() *Router {
// This will send all incoming requests to the router. // This will send all incoming requests to the router.
type Router struct { type Router struct {
// Configurable Handler to be used when no route matches. // Configurable Handler to be used when no route matches.
// This can be used to render your own 404 Not Found errors.
NotFoundHandler http.Handler NotFoundHandler http.Handler
// Configurable Handler to be used when the request method does not match the route. // Configurable Handler to be used when the request method does not match the route.
// This can be used to render your own 405 Method Not Allowed errors.
MethodNotAllowedHandler http.Handler MethodNotAllowedHandler http.Handler
// Routes to be matched, in order. // Routes to be matched, in order.
@ -58,8 +61,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 +113,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 +197,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 +428,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
@ -437,21 +437,22 @@ func Vars(r *http.Request) map[string]string {
// CurrentRoute returns the matched route for the current request, if any. // CurrentRoute returns the matched route for the current request, if any.
// This only works when called inside the handler of the matched route // 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 // 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 // after the handler returns.
// 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

@ -22,10 +22,10 @@ type routeRegexpOptions struct {
type regexpType int type regexpType int
const ( const (
regexpTypePath regexpType = 0 regexpTypePath regexpType = iota
regexpTypeHost regexpType = 1 regexpTypeHost
regexpTypePrefix regexpType = 2 regexpTypePrefix
regexpTypeQuery regexpType = 3 regexpTypeQuery
) )
// newRouteRegexp parses a route template and returns a routeRegexp, // newRouteRegexp parses a route template and returns a routeRegexp,
@ -181,7 +181,8 @@ 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 { if r.regexpType == regexpTypeQuery {
return r.matchQueryString(req) return r.matchQueryString(req)
} }
@ -190,7 +191,6 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
path = req.URL.EscapedPath() path = req.URL.EscapedPath()
} }
return r.regexp.MatchString(path) return r.regexp.MatchString(path)
}
} }
// url builds a URL part using the given values. // url builds a URL part using the given values.
@ -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))
} }
@ -288,6 +325,12 @@ func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
// Store host variables. // Store host variables.
if v.host != nil { if v.host != nil {
host := getHost(req) 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) matches := v.host.regexp.FindStringSubmatchIndex(host)
if len(matches) > 0 { if len(matches) > 0 {
extractVars(host, matches, v.host.varsN, m.Vars) extractVars(host, matches, v.host.varsN, m.Vars)

View file

@ -64,8 +64,18 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
match.MatchErr = nil match.MatchErr = nil
} }
matchErr = nil matchErr = nil // nolint:ineffassign
return false return false
} else {
// Multiple routes may share the same path but use different HTTP methods. For instance:
// Route 1: POST "/users/{id}".
// Route 2: GET "/users/{id}", parameters: "id": "[0-9]+".
//
// The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2",
// The router should return a "Not Found" error as no route fully matches this request.
if match.MatchErr == ErrMethodMismatch {
match.MatchErr = nil
}
} }
} }
@ -74,7 +84,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
@ -230,7 +240,7 @@ func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
// Headers adds a matcher for request header values. // Headers adds a matcher for request header values.
// It accepts a sequence of key/value pairs to be matched. For example: // It accepts a sequence of key/value pairs to be matched. For example:
// //
// r := mux.NewRouter() // r := mux.NewRouter().NewRoute()
// r.Headers("Content-Type", "application/json", // r.Headers("Content-Type", "application/json",
// "X-Requested-With", "XMLHttpRequest") // "X-Requested-With", "XMLHttpRequest")
// //
@ -255,7 +265,7 @@ func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex // HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
// support. For example: // support. For example:
// //
// r := mux.NewRouter() // r := mux.NewRouter().NewRoute()
// r.HeadersRegexp("Content-Type", "application/(text|json)", // r.HeadersRegexp("Content-Type", "application/(text|json)",
// "X-Requested-With", "XMLHttpRequest") // "X-Requested-With", "XMLHttpRequest")
// //
@ -283,7 +293,7 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route {
// //
// For example: // For example:
// //
// r := mux.NewRouter() // r := mux.NewRouter().NewRoute()
// r.Host("www.example.com") // r.Host("www.example.com")
// r.Host("{subdomain}.domain.com") // r.Host("{subdomain}.domain.com")
// r.Host("{subdomain:[a-z]+}.domain.com") // r.Host("{subdomain:[a-z]+}.domain.com")
@ -342,7 +352,7 @@ func (r *Route) Methods(methods ...string) *Route {
// //
// For example: // For example:
// //
// r := mux.NewRouter() // r := mux.NewRouter().NewRoute()
// r.Path("/products/").Handler(ProductsHandler) // r.Path("/products/").Handler(ProductsHandler)
// r.Path("/products/{key}").Handler(ProductsHandler) // r.Path("/products/{key}").Handler(ProductsHandler)
// r.Path("/articles/{category}/{id:[0-9]+}"). // r.Path("/articles/{category}/{id:[0-9]+}").
@ -377,7 +387,7 @@ func (r *Route) PathPrefix(tpl string) *Route {
// It accepts a sequence of key/value pairs. Values may define variables. // It accepts a sequence of key/value pairs. Values may define variables.
// For example: // For example:
// //
// r := mux.NewRouter() // r := mux.NewRouter().NewRoute()
// r.Queries("foo", "bar", "id", "{id:[0-9]+}") // r.Queries("foo", "bar", "id", "{id:[0-9]+}")
// //
// The above route will only match if the URL contains the defined queries // The above route will only match if the URL contains the defined queries
@ -412,11 +422,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)
@ -454,7 +483,7 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
// //
// It will test the inner routes only if the parent route matched. For example: // It will test the inner routes only if the parent route matched. For example:
// //
// r := mux.NewRouter() // r := mux.NewRouter().NewRoute()
// s := r.Host("www.example.com").Subrouter() // s := r.Host("www.example.com").Subrouter()
// s.HandleFunc("/products/", ProductsHandler) // s.HandleFunc("/products/", ProductsHandler)
// s.HandleFunc("/products/{key}", ProductHandler) // s.HandleFunc("/products/{key}", ProductHandler)
@ -493,8 +522,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 +531,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().NewRoute()
// 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 +671,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 +690,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)
} }
@ -692,6 +728,25 @@ func (r *Route) GetHostTemplate() (string, error) {
return r.regexp.host.template, nil return r.regexp.host.template, nil
} }
// GetVarNames returns the names of all variables added by regexp matchers
// These can be used to know which route variables should be passed into r.URL()
func (r *Route) GetVarNames() ([]string, error) {
if r.err != nil {
return nil, r.err
}
var varNames []string
if r.regexp.host != nil {
varNames = append(varNames, r.regexp.host.varsN...)
}
if r.regexp.path != nil {
varNames = append(varNames, r.regexp.path.varsN...)
}
for _, regx := range r.regexp.queries {
varNames = append(varNames, regx.varsN...)
}
return varNames, nil
}
// prepareVars converts the route variable pairs into a map. If the route has a // prepareVars converts the route variable pairs into a map. If the route has a
// BuildVarsFunc, it is invoked. // BuildVarsFunc, it is invoked.
func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {

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

4
vendor/modules.txt vendored
View file

@ -13,12 +13,10 @@ github.com/ghodss/yaml
## explicit ## explicit
github.com/go-chi/chi/v5 github.com/go-chi/chi/v5
github.com/go-chi/chi/v5/middleware github.com/go-chi/chi/v5/middleware
# github.com/gofrs/uuid v3.2.0+incompatible
## explicit
# github.com/gofrs/uuid/v5 v5.0.0 # github.com/gofrs/uuid/v5 v5.0.0
## explicit ## explicit
github.com/gofrs/uuid/v5 github.com/gofrs/uuid/v5
# github.com/gorilla/mux v1.7.3 # github.com/gorilla/mux v1.8.1
## explicit ## explicit
github.com/gorilla/mux github.com/gorilla/mux
# github.com/kr/pretty v0.1.0 # github.com/kr/pretty v0.1.0