update limiter deps
This commit is contained in:
+2
-2
@@ -7,8 +7,8 @@ require (
|
||||
github.com/PuerkitoBio/goquery v1.5.1
|
||||
github.com/alecthomas/chroma v0.7.2
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/didip/tollbooth/v6 v6.0.1
|
||||
github.com/didip/tollbooth_chi v0.0.0-20200524181329-8b84cd7183d9
|
||||
github.com/didip/tollbooth/v6 v6.1.0
|
||||
github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21
|
||||
github.com/go-chi/chi v4.1.1+incompatible
|
||||
github.com/go-chi/cors v1.1.1
|
||||
github.com/go-chi/render v1.0.1
|
||||
|
||||
+4
-6
@@ -40,8 +40,12 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/didip/tollbooth/v6 v6.0.1 h1:QvLvRpB1G2bzKvkRze0muMUBlGN9H1z7tJ4DH4ypWOU=
|
||||
github.com/didip/tollbooth/v6 v6.0.1/go.mod h1:j2pKs+JQ5PvU/K4jFnrnwntrmfUbYLJE5oSdxR37FD0=
|
||||
github.com/didip/tollbooth/v6 v6.1.0 h1:ZS2fNa9JhFdRSJCj3+V12VfuUifYrGB4Z0jSwXmKMeE=
|
||||
github.com/didip/tollbooth/v6 v6.1.0/go.mod h1:xjcse6CTHCLuOkzsWrEgdy9WPJFv+p/x6v+MyfP+O9s=
|
||||
github.com/didip/tollbooth_chi v0.0.0-20200524181329-8b84cd7183d9 h1:gTh8fKuI/yLqQtZEPlDX3ZGsiTPZIe0ADHsxXSbwO1I=
|
||||
github.com/didip/tollbooth_chi v0.0.0-20200524181329-8b84cd7183d9/go.mod h1:YWyIfq3y4ArRfWZ9XksmuusP+7Mad+T0iFZ0kv0XG/M=
|
||||
github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21 h1:x7YpwKSBIBcKe9I3aTNOqgSyJ6QKDdtOxnEkxBTsi9w=
|
||||
github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21/go.mod h1:0ZVa6kSzS011nfTC1rELyxK4tjVf6vqBnOv7oY2KlsA=
|
||||
github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
|
||||
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc=
|
||||
@@ -58,12 +62,6 @@ github.com/go-chi/cors v1.1.1 h1:eHuqxsIw89iXcWnWUN8R72JMibABJTN/4IOYI5WERvw=
|
||||
github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
|
||||
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||
github.com/go-pkgz/auth v1.13.1 h1:+N9f2fSbvczMzOEaiGSxz1018JtvIaVM017GbmF7dF4=
|
||||
github.com/go-pkgz/auth v1.13.1/go.mod h1:1GVd61pXZcuJ0ZnOUdCTY08V8SreO7MJtsvEd5/WEWA=
|
||||
github.com/go-pkgz/auth v1.13.2-0.20210114092942-d2b22c3f3881 h1:BlRK66nY8N7xkV51kQ0XEQD3No4Txk6QGedza5uaaGI=
|
||||
github.com/go-pkgz/auth v1.13.2-0.20210114092942-d2b22c3f3881/go.mod h1:1GVd61pXZcuJ0ZnOUdCTY08V8SreO7MJtsvEd5/WEWA=
|
||||
github.com/go-pkgz/auth v1.13.2-0.20210115173516-bd39e5e3cfb7 h1:+ZuYxKuAg54eEXTkkArAti0epGTUDX44d16kInxqCX4=
|
||||
github.com/go-pkgz/auth v1.13.2-0.20210115173516-bd39e5e3cfb7/go.mod h1:1GVd61pXZcuJ0ZnOUdCTY08V8SreO7MJtsvEd5/WEWA=
|
||||
github.com/go-pkgz/auth v1.14.0 h1:fVNEcn9ry8v45sclcXo2wzrFaXtWX+Q5Th2qm7FRGX4=
|
||||
github.com/go-pkgz/auth v1.14.0/go.mod h1:1GVd61pXZcuJ0ZnOUdCTY08V8SreO7MJtsvEd5/WEWA=
|
||||
github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc=
|
||||
|
||||
+2
@@ -4,5 +4,7 @@ go 1.12
|
||||
|
||||
require (
|
||||
github.com/go-pkgz/expirable-cache v0.0.3
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
||||
+7
@@ -2,6 +2,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc=
|
||||
github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ=
|
||||
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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -13,5 +18,7 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omN
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
+17
@@ -61,6 +61,9 @@ type Limiter struct {
|
||||
// A function to call when a request is rejected.
|
||||
onLimitReached func(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// An option to write back what you want upon reaching a limit.
|
||||
overrideDefaultResponseWriter bool
|
||||
|
||||
// List of places to look up IP address.
|
||||
// Default is "RemoteAddr", "X-Forwarded-For", "X-Real-IP".
|
||||
// You can rearrange the order as you like.
|
||||
@@ -261,6 +264,20 @@ func (l *Limiter) ExecOnLimitReached(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetOverrideDefaultResponseWriter is a thread-safe way of setting the response writer override variable.
|
||||
func (l *Limiter) SetOverrideDefaultResponseWriter(override bool) {
|
||||
l.Lock()
|
||||
l.overrideDefaultResponseWriter = override
|
||||
l.Unlock()
|
||||
}
|
||||
|
||||
// GetOverrideDefaultResponseWriter is a thread-safe way of getting the response writer override variable.
|
||||
func (l *Limiter) GetOverrideDefaultResponseWriter() bool {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
return l.overrideDefaultResponseWriter
|
||||
}
|
||||
|
||||
// SetIPLookups is thread-safe way of setting list of places to look up IP address.
|
||||
func (l *Limiter) SetIPLookups(ipLookups []string) *Limiter {
|
||||
l.Lock()
|
||||
|
||||
+159
-20
@@ -16,7 +16,12 @@ import (
|
||||
func setResponseHeaders(lmt *limiter.Limiter, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("X-Rate-Limit-Limit", fmt.Sprintf("%.2f", lmt.GetMax()))
|
||||
w.Header().Add("X-Rate-Limit-Duration", "1")
|
||||
w.Header().Add("X-Rate-Limit-Request-Forwarded-For", r.Header.Get("X-Forwarded-For"))
|
||||
|
||||
xForwardedFor := r.Header.Get("X-Forwarded-For")
|
||||
if strings.TrimSpace(xForwardedFor) != "" {
|
||||
w.Header().Add("X-Rate-Limit-Request-Forwarded-For", xForwardedFor)
|
||||
}
|
||||
|
||||
w.Header().Add("X-Rate-Limit-Request-Remote-Addr", r.RemoteAddr)
|
||||
}
|
||||
|
||||
@@ -38,17 +43,142 @@ func LimitByKeys(lmt *limiter.Limiter, keys []string) *errors.HTTPError {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShouldSkipLimiter is a series of filter that decides if request should be limited or not.
|
||||
func ShouldSkipLimiter(lmt *limiter.Limiter, r *http.Request) bool {
|
||||
// ---------------------------------
|
||||
// Filter by remote ip
|
||||
// If we are unable to find remoteIP, skip limiter
|
||||
remoteIP := libstring.RemoteIP(lmt.GetIPLookups(), lmt.GetForwardedForIndexFromBehind(), r)
|
||||
if remoteIP == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// Filter by request method
|
||||
lmtMethods := lmt.GetMethods()
|
||||
lmtMethodsIsSet := len(lmtMethods) > 0
|
||||
|
||||
if lmtMethodsIsSet {
|
||||
// If request does not contain all of the methods in limiter,
|
||||
// skip limiter
|
||||
requestMethodDefinedInLimiter := libstring.StringInSlice(lmtMethods, r.Method)
|
||||
|
||||
if !requestMethodDefinedInLimiter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// Filter by request headers
|
||||
lmtHeaders := lmt.GetHeaders()
|
||||
lmtHeadersIsSet := len(lmtHeaders) > 0
|
||||
|
||||
if lmtHeadersIsSet {
|
||||
// If request does not contain all of the headers in limiter,
|
||||
// skip limiter
|
||||
requestHeadersDefinedInLimiter := false
|
||||
|
||||
for headerKey := range lmtHeaders {
|
||||
reqHeaderValue := r.Header.Get(headerKey)
|
||||
if reqHeaderValue != "" {
|
||||
requestHeadersDefinedInLimiter = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !requestHeadersDefinedInLimiter {
|
||||
return true
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// If request contains the header key but not the values,
|
||||
// skip limiter
|
||||
requestHeadersDefinedInLimiter = false
|
||||
|
||||
for headerKey, headerValues := range lmtHeaders {
|
||||
for _, headerValue := range headerValues {
|
||||
if r.Header.Get(headerKey) == headerValue {
|
||||
requestHeadersDefinedInLimiter = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !requestHeadersDefinedInLimiter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// Filter by context values
|
||||
lmtContextValues := lmt.GetContextValues()
|
||||
lmtContextValuesIsSet := len(lmtContextValues) > 0
|
||||
|
||||
if lmtContextValuesIsSet {
|
||||
// If request does not contain all of the contexts in limiter,
|
||||
// skip limiter
|
||||
requestContextValuesDefinedInLimiter := false
|
||||
|
||||
for contextKey := range lmtContextValues {
|
||||
reqContextValue := fmt.Sprintf("%v", r.Context().Value(contextKey))
|
||||
if reqContextValue != "" {
|
||||
requestContextValuesDefinedInLimiter = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !requestContextValuesDefinedInLimiter {
|
||||
return true
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// If request contains the context key but not the values,
|
||||
// skip limiter
|
||||
requestContextValuesDefinedInLimiter = false
|
||||
|
||||
for contextKey, contextValues := range lmtContextValues {
|
||||
for _, contextValue := range contextValues {
|
||||
if r.Header.Get(contextKey) == contextValue {
|
||||
requestContextValuesDefinedInLimiter = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !requestContextValuesDefinedInLimiter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// Filter by basic auth usernames
|
||||
lmtBasicAuthUsers := lmt.GetBasicAuthUsers()
|
||||
lmtBasicAuthUsersIsSet := len(lmtBasicAuthUsers) > 0
|
||||
|
||||
if lmtBasicAuthUsersIsSet {
|
||||
// If request does not contain all of the basic auth users in limiter,
|
||||
// skip limiter
|
||||
requestAuthUsernameDefinedInLimiter := false
|
||||
|
||||
username, _, ok := r.BasicAuth()
|
||||
if ok && libstring.StringInSlice(lmtBasicAuthUsers, username) {
|
||||
requestAuthUsernameDefinedInLimiter = true
|
||||
}
|
||||
|
||||
if !requestAuthUsernameDefinedInLimiter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// BuildKeys generates a slice of keys to rate-limit by given limiter and request structs.
|
||||
func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
|
||||
remoteIP := libstring.RemoteIP(lmt.GetIPLookups(), lmt.GetForwardedForIndexFromBehind(), r)
|
||||
path := r.URL.Path
|
||||
sliceKeys := make([][]string, 0)
|
||||
|
||||
// Don't BuildKeys if remoteIP is blank.
|
||||
if remoteIP == "" {
|
||||
return sliceKeys
|
||||
}
|
||||
|
||||
lmtMethods := lmt.GetMethods()
|
||||
lmtHeaders := lmt.GetHeaders()
|
||||
lmtContextValues := lmt.GetContextValues()
|
||||
@@ -58,11 +188,6 @@ func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
|
||||
lmtContextValuesIsSet := len(lmtContextValues) > 0
|
||||
lmtBasicAuthUsersIsSet := len(lmtBasicAuthUsers) > 0
|
||||
|
||||
method := ""
|
||||
if lmtMethods != nil && libstring.StringInSlice(lmtMethods, r.Method) {
|
||||
method = r.Method
|
||||
}
|
||||
|
||||
usernameToLimit := ""
|
||||
if lmtBasicAuthUsersIsSet {
|
||||
username, _, ok := r.BasicAuth()
|
||||
@@ -93,8 +218,6 @@ func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
headerValuesToLimit = append(headerValuesToLimit, []string{"", ""})
|
||||
}
|
||||
|
||||
contextValuesToLimit := [][]string{}
|
||||
@@ -106,11 +229,11 @@ func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
|
||||
}
|
||||
|
||||
if len(contextValues) == 0 {
|
||||
// If header values are empty, rate-limit all request containing headerKey.
|
||||
// If context values are empty, rate-limit all request containing contextKey.
|
||||
contextValuesToLimit = append(contextValuesToLimit, []string{contextKey, reqContextValue})
|
||||
|
||||
} else {
|
||||
// If header values are not empty, rate-limit all request with headerKey and headerValues.
|
||||
// If context values are not empty, rate-limit all request with contextKey and contextValues.
|
||||
for _, contextValue := range contextValues {
|
||||
if reqContextValue == contextValue {
|
||||
contextValuesToLimit = append(contextValuesToLimit, []string{contextKey, contextValue})
|
||||
@@ -119,16 +242,24 @@ func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
contextValuesToLimit = append(contextValuesToLimit, []string{"", ""})
|
||||
}
|
||||
|
||||
sliceKey := []string{remoteIP, path}
|
||||
|
||||
sliceKey = append(sliceKey, lmtMethods...)
|
||||
|
||||
for _, header := range headerValuesToLimit {
|
||||
for _, contextValue := range contextValuesToLimit {
|
||||
sliceKeys = append(sliceKeys, []string{remoteIP, path, method, header[0], header[1], contextValue[0], contextValue[1], usernameToLimit})
|
||||
}
|
||||
sliceKey = append(sliceKey, header[0], header[1])
|
||||
}
|
||||
|
||||
for _, contextValue := range contextValuesToLimit {
|
||||
sliceKey = append(sliceKey, contextValue[0], contextValue[1])
|
||||
}
|
||||
|
||||
sliceKey = append(sliceKey, usernameToLimit)
|
||||
|
||||
sliceKeys = append(sliceKeys, sliceKey)
|
||||
|
||||
return sliceKeys
|
||||
}
|
||||
|
||||
@@ -137,6 +268,11 @@ func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
|
||||
func LimitByRequest(lmt *limiter.Limiter, w http.ResponseWriter, r *http.Request) *errors.HTTPError {
|
||||
setResponseHeaders(lmt, w, r)
|
||||
|
||||
shouldSkip := ShouldSkipLimiter(lmt, r)
|
||||
if shouldSkip {
|
||||
return nil
|
||||
}
|
||||
|
||||
sliceKeys := BuildKeys(lmt, r)
|
||||
|
||||
// Loop sliceKeys and check if one of them has error.
|
||||
@@ -156,6 +292,9 @@ func LimitHandler(lmt *limiter.Limiter, next http.Handler) http.Handler {
|
||||
httpError := LimitByRequest(lmt, w, r)
|
||||
if httpError != nil {
|
||||
lmt.ExecOnLimitReached(w, r)
|
||||
if lmt.GetOverrideDefaultResponseWriter() {
|
||||
return
|
||||
}
|
||||
w.Header().Add("Content-Type", lmt.GetMessageContentType())
|
||||
w.WriteHeader(httpError.StatusCode)
|
||||
w.Write([]byte(httpError.Message))
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
module github.com/didip/tollbooth_chi
|
||||
|
||||
go 1.14
|
||||
|
||||
require github.com/didip/tollbooth/v6 v6.0.1
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/didip/tollbooth v1.0.0 h1:nVIxVp0Jj75TVxTwUdbRzC0qO0K/LRZudetGemvTCxQ=
|
||||
github.com/didip/tollbooth v4.0.2+incompatible h1:fVSa33JzSz0hoh2NxpwZtksAzAgd7zjmGO20HCZtF4M=
|
||||
github.com/didip/tollbooth/v6 v6.0.1 h1:QvLvRpB1G2bzKvkRze0muMUBlGN9H1z7tJ4DH4ypWOU=
|
||||
github.com/didip/tollbooth/v6 v6.0.1/go.mod h1:j2pKs+JQ5PvU/K4jFnrnwntrmfUbYLJE5oSdxR37FD0=
|
||||
github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc=
|
||||
github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
Vendored
+2
-2
@@ -68,13 +68,13 @@ github.com/dghubble/oauth1/twitter
|
||||
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
## explicit
|
||||
github.com/dgrijalva/jwt-go
|
||||
# github.com/didip/tollbooth/v6 v6.0.1
|
||||
# github.com/didip/tollbooth/v6 v6.1.0
|
||||
## explicit
|
||||
github.com/didip/tollbooth/v6
|
||||
github.com/didip/tollbooth/v6/errors
|
||||
github.com/didip/tollbooth/v6/libstring
|
||||
github.com/didip/tollbooth/v6/limiter
|
||||
# github.com/didip/tollbooth_chi v0.0.0-20200524181329-8b84cd7183d9
|
||||
# github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21
|
||||
## explicit
|
||||
github.com/didip/tollbooth_chi
|
||||
# github.com/dlclark/regexp2 v1.1.6
|
||||
|
||||
Reference in New Issue
Block a user