⬆️ Upgraded JWX package

for secure and fast decoding access tokens
develop
Maxim Lebedev 2022-06-09 23:53:45 +05:00
parent dcf9e3c2ca
commit 531b14524c
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
195 changed files with 10853 additions and 8787 deletions

13
go.mod
View File

@ -4,11 +4,11 @@ go 1.18
require (
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/brianvoe/gofakeit/v6 v6.15.0
github.com/brianvoe/gofakeit/v6 v6.16.0
github.com/fasthttp/router v1.4.10
github.com/goccy/go-json v0.9.7
github.com/jmoiron/sqlx v1.3.5
github.com/lestrrat-go/jwx v1.2.25
github.com/lestrrat-go/jwx/v2 v2.0.2
github.com/spf13/viper v1.12.0
github.com/stretchr/testify v1.7.2
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
@ -21,7 +21,7 @@ require (
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6
modernc.org/sqlite v1.17.3
source.toby3d.me/toby3d/form v0.3.0
source.toby3d.me/toby3d/middleware v0.9.1
source.toby3d.me/toby3d/middleware v0.9.2
willnorris.com/go/microformats v1.1.1
)
@ -36,9 +36,9 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.15.6 // indirect
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.1 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
@ -47,7 +47,6 @@ require (
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
github.com/philhofer/fwd v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d // indirect
@ -62,10 +61,10 @@ require (
go4.org/intern v0.0.0-20220301175310-a089fc204883 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/tools v0.1.11 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

28
go.sum
View File

@ -47,8 +47,8 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/brianvoe/gofakeit/v6 v6.15.0 h1:lJPGJZ2/07TRGDazyTzD5b18N3y4tmmJpdhCUw18FlI=
github.com/brianvoe/gofakeit/v6 v6.15.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
github.com/brianvoe/gofakeit/v6 v6.16.0 h1:EelCqtfArd8ppJ0z+TpOxXH8sVWNPBadPNdCDSMMw7k=
github.com/brianvoe/gofakeit/v6 v6.16.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@ -62,7 +62,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
@ -185,18 +184,16 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/httprc v1.0.1 h1:Cnc4NxIySph38pQPzKbjg5OkKsGR/Cf5xcWt5OlSUDI=
github.com/lestrrat-go/httprc v1.0.1/go.mod h1:5Ml+nB++j6IC0e6LzefJnrpMQDKgDwDCaIQQzhbqhJM=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx v1.2.25 h1:tAx93jN2SdPvFn08fHNAhqFJazn5mBBOB8Zli0g0otA=
github.com/lestrrat-go/jwx v1.2.25/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY=
github.com/lestrrat-go/jwx/v2 v2.0.2 h1:wkq9jwCkF3xrykISzn0Eksd7NEMOZ9yvCdnEpovIJX8=
github.com/lestrrat-go/jwx/v2 v2.0.2/go.mod h1:xV8+xRcrKbmnScV8adOzUuuTrL8aAZJoY4q2JAqIYU8=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@ -230,7 +227,6 @@ github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -342,8 +338,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -518,8 +514,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -685,7 +681,7 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
source.toby3d.me/toby3d/form v0.3.0 h1:kI8apdFeVr+koqTTGVoIRiR5NMqjrhCJlajYlu+1bVw=
source.toby3d.me/toby3d/form v0.3.0/go.mod h1:drlHMC+j/gb5zsttCSwx8qcYsbaRW+wFfE8bK6y+oeY=
source.toby3d.me/toby3d/middleware v0.9.1 h1:MjXDs57SbrIndEVFJHN36ZiO2OgnXNSajrLqa87vzZQ=
source.toby3d.me/toby3d/middleware v0.9.1/go.mod h1:Z67Z26wyW5USWnAYBYiA6mLubyzsHkbY6sWuIzs4304=
source.toby3d.me/toby3d/middleware v0.9.2 h1:HInjjZaN7GTqlWq32XscJs4Wf0taFG6OhyTAJrED1vA=
source.toby3d.me/toby3d/middleware v0.9.2/go.mod h1:MWedNnEpLCOk2rgjlfjpkn38t+1j53htSrp3lf6pC34=
willnorris.com/go/microformats v1.1.1 h1:h5tk2luq6KBIRcwMGdksxdeea4GGuWrRFie5460OAbo=
willnorris.com/go/microformats v1.1.1/go.mod h1:kvVnWrkkEscVAIITCEoiTX66Hcyg59C7q0E49mb9TJ0=

View File

@ -95,6 +95,7 @@ func initLookup() {
addWeightedLookup()
addMinecraftLookup()
addCelebrityLookup()
addDatabaseSQLLookup()
}
// NewMapParams will create a new MapParams

157
vendor/github.com/brianvoe/gofakeit/v6/sql.go generated vendored Normal file
View File

@ -0,0 +1,157 @@
package gofakeit
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"strings"
)
type SQLOptions struct {
Table string `json:"table" xml:"table"` // Table name we are inserting into
Count int `json:"count" xml:"count"` // How many entries (tuples) we're generating
Fields []Field `json:"fields" xml:"fields"` // The fields to be generated
}
func SQL(so *SQLOptions) (string, error) { return sqlFunc(globalFaker.Rand, so) }
func (f *Faker) SQL(so *SQLOptions) (string, error) { return sqlFunc(f.Rand, so) }
func sqlFunc(r *rand.Rand, so *SQLOptions) (string, error) {
if so.Table == "" {
return "", errors.New("must provide table name to generate SQL")
}
if so.Fields == nil || len(so.Fields) <= 0 {
return "", errors.New(("must pass fields in order to generate SQL queries"))
}
if so.Count <= 0 {
return "", errors.New("must have entry count")
}
var sb strings.Builder
sb.WriteString("INSERT INTO " + so.Table + " ")
// Loop through each field and put together column names
var cols []string
for _, f := range so.Fields {
cols = append(cols, f.Name)
}
sb.WriteString("(" + strings.Join(cols, ", ") + ")")
sb.WriteString(" VALUES ")
for i := 0; i < so.Count; i++ {
// Start opening value
sb.WriteString("(")
// Now, we need to add all of our fields
var endStr string
for ii, field := range so.Fields {
// Set end of value string
endStr = ", "
if ii == len(so.Fields)-1 {
endStr = ""
}
// If autoincrement, add based upon loop
if field.Function == "autoincrement" {
sb.WriteString(fmt.Sprintf("%d%s", i+1, endStr))
continue
}
// Get the function info for the field
funcInfo := GetFuncLookup(field.Function)
if funcInfo == nil {
return "", errors.New("invalid function, " + field.Function + " does not exist")
}
// Generate the value
val, err := funcInfo.Generate(r, &field.Params, funcInfo)
if err != nil {
return "", err
}
// Convert the output value to the proper SQL type
convertType := sqlConvertType(funcInfo.Output, val)
// If its the last field, we need to close the value
sb.WriteString(convertType + endStr)
}
// If its the last value, we need to close the value
if i == so.Count-1 {
sb.WriteString(");")
} else {
sb.WriteString("),")
}
}
return sb.String(), nil
}
// sqlConvertType will take in a type and value and convert it to the proper SQL type
func sqlConvertType(t string, val interface{}) string {
switch t {
case "string":
return `'` + fmt.Sprintf("%v", val) + `'`
case "[]byte":
return `'` + fmt.Sprintf("%s", val) + `'`
default:
return fmt.Sprintf("%v", val)
}
}
func addDatabaseSQLLookup() {
AddFuncLookup("sql", Info{
Display: "SQL",
Category: "database",
Description: "Generates an object or an array of objects in json format",
Example: `INSERT INTO people
(id, first_name, price, age, created_at)
VALUES
(1, 'Markus', 804.92, 21, '1937-01-30 07:58:01'),
(2, 'Santino', 235.13, 40, '1964-07-07 22:25:40');`,
Output: "string",
ContentType: "application/sql",
Params: []Param{
{Field: "table", Display: "Table", Type: "string", Description: "Name of the table to insert into"},
{Field: "count", Display: "Count", Type: "int", Default: "100", Description: "Number of inserts to generate"},
{Field: "fields", Display: "Fields", Type: "[]Field", Description: "Fields containing key name and function to run in json format"},
},
Generate: func(r *rand.Rand, m *MapParams, info *Info) (interface{}, error) {
so := SQLOptions{}
table, err := info.GetString(m, "table")
if err != nil {
return nil, err
}
so.Table = table
count, err := info.GetInt(m, "count")
if err != nil {
return nil, err
}
so.Count = count
fieldsStr, err := info.GetStringArray(m, "fields")
if err != nil {
return nil, err
}
// Check to make sure fields has length
if len(fieldsStr) > 0 {
so.Fields = make([]Field, len(fieldsStr))
for i, f := range fieldsStr {
// Unmarshal fields string into fields array
err = json.Unmarshal([]byte(f), &so.Fields[i])
if err != nil {
return nil, err
}
}
}
return sqlFunc(r, &so)
},
})
}

View File

@ -1,7 +0,0 @@
issues:
exclude-rules:
- path: /*_example_test.go
linters:
- errcheck
- forbidigo

View File

@ -1,8 +0,0 @@
v2.0.8 - 28 Feb 2021
* Fix possible goroutine leak (#30)
v2.0.7 - 26 Jan 2021
* Cosmetic go.mod / go.sum changes
v2.0.6 - 25 Jan 2021
* Add jitter to constant backoff

View File

@ -1,183 +0,0 @@
# backoff ![](https://github.com/lestrrat-go/backoff/workflows/CI/badge.svg) [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/backoff/v2.svg)](https://pkg.go.dev/github.com/lestrrat-go/backoff/v2)
Idiomatic backoff for Go
This library is an implementation of backoff algorithm for retrying operations
in an idiomatic Go way. It respects `context.Context` natively, and the critical
notifications are done through *channel operations*, allowing you to write code
that is both more explicit and flexibile.
For a longer discussion, [please read this article](https://medium.com/@lestrrat/yak-shaving-with-backoff-libraries-in-go-80240f0aa30c)
# IMPORT
```go
import "github.com/lestrrat-go/backoff/v2"
```
# SYNOPSIS
```go
func ExampleExponential() {
p := backoff.Exponential(
backoff.WithMinInterval(time.Second),
backoff.WithMaxInterval(time.Minute),
backoff.WithJitterFactor(0.05),
)
flakyFunc := func(a int) (int, error) {
// silly function that only succeeds if the current call count is
// divisible by either 3 or 5 but not both
switch {
case a%15 == 0:
return 0, errors.New(`invalid`)
case a%3 == 0 || a%5 == 0:
return a, nil
}
return 0, errors.New(`invalid`)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
retryFunc := func(v int) (int, error) {
b := p.Start(ctx)
for backoff.Continue(b) {
x, err := flakyFunc(v)
if err == nil {
return x, nil
}
}
return 0, errors.New(`failed to get value`)
}
retryFunc(15)
}
```
# POLICIES
Policy objects describe a backoff policy, and are factories to create backoff Controller objects.
Controller objects does the actual coordination.
Create a new controller for each invocation of a backoff enabled operation.
This way the controller object is protected from concurrent access (if you have one) and can easily be discarded
## Null
A null policy means there's no backoff.
For example, if you were to support both using and not using a backoff in your code you can say
```go
var p backoff.Policy
if useBackoff {
p = backoff.Exponential(...)
} else {
p = backoff.Null()
}
c := p.Start(ctx)
for backoff.Continue(c) {
if err := doSomething(); err != nil {
continue
}
return
}
```
Instead of
```go
if useBackoff {
p := backoff.Exponential(...)
c := p.Start(ctx)
for backoff.Continue(c) {
if err := doSomething(); err != nil {
continue
}
return
}
} else {
if err := doSomething(); err != nil {
continue
}
}
```
## Constant
A constant policy implements are backoff where the intervals are always the same
## Exponential
This is the most "common" of the backoffs. Intervals between calls are spaced out such that as you keep retrying, the intervals keep increasing.
# FAQ
## I'm getting "package github.com/lestrrat-go/backoff/v2: no Go files in /go/src/github.com/lestrrat-go/backoff/v2"
You are using Go in GOPATH mode, which was the way before [Go Modules](https://blog.golang.org/using-go-modules) were introduced in Go 1.11 (Aug 2018).
GOPATH has slowly been phased out, and in Go 1.14 onwards, Go Modules pretty much Just Work.
Go 1.16 introduced more visible changes that forces users to be aware of the existance of go.mod files.
The short answer when you you get the above error is: **Migrate to using Go Modules**.
This is simple: All you need to do is to include a go.mod (and go.sum) file to your library or app.
For example, if you have previously been doing this:
```
git clone git@github.com:myusername/myawesomeproject.git
cd myawesomeproject
go get ./...
```
First include go.mod and go.sum in your repository:
```
git clone git@github.com:myusername/myawesomeproject.git
cd myawesomeproject
go mod init
go mod tidy
git add go.mod go.sum
git commit -m "Add go.mod and go.sum" -a
git push
```
Then from subsequent calls:
```
git clone git@github.com:myusername/myawesomeproject.git
cd myawesomeproject
go build # or go test, or go run, or whatever.
```
This will tell go to respect tags, and will automatically pick up the latest version of github.com/lestrrat-go/backoff
If you really can't do this, then the quick and dirty workaround is to just copy the files over to /v2 directory of this library
```
BACKOFF=github.com/lestrrat-go/backoff
go get github.com/lestrrat-go/backoff
if [[ if ! -d "$GOPATH/src/$BACKOFF/v2" ]]; then
mkdir "$GOPATH/src/$BACKOFF/v2" # just to make sure it exists
fi
cp "$GOPATH/src/$BACKOFF/*.go" "$GOPATH/src/$BACKOFF/v2"
git clone git@github.com:myusername/myawesomeproject.git
cd myawesomeproject
go get ./...
```
## Why won't you just add the files in /v2?
Because it's hard to maintain multiple sources of truth. Sorry, I don't get paid to do this.
I will not hold anything against you if you decided to fork this to your repository, and move files to your own /v2 directory.
Then, if you have a go.mod in your app, you can just do
```
go mod edit -replace github.com/lestrrat-go/backoff/v2=github.com/myusername/myawesomemfork/v2
```
Oh, wait, then you already have go.mod, so this is a non-issue.
...Yeah, just migrate to using go modules, please?

View File

@ -1,31 +0,0 @@
package backoff
// Null creates a new NullPolicy object
func Null() Policy {
return NewNull()
}
// Constant creates a new ConstantPolicy object
func Constant(options ...Option) Policy {
return NewConstantPolicy(options...)
}
// Constant creates a new ExponentialPolicy object
func Exponential(options ...ExponentialOption) Policy {
return NewExponentialPolicy(options...)
}
// Continue is a convenience function to check when we can fire
// the next invocation of the desired backoff code
//
// for backoff.Continue(c) {
// ... your code ...
// }
func Continue(c Controller) bool {
select {
case <-c.Done():
return false
case _, ok := <-c.Next():
return ok
}
}

View File

@ -1,66 +0,0 @@
package backoff
import (
"context"
"time"
)
type ConstantInterval struct {
interval time.Duration
jitter jitter
}
func NewConstantInterval(options ...ConstantOption) *ConstantInterval {
jitterFactor := 0.0
interval := time.Minute
var rng Random
for _, option := range options {
switch option.Ident() {
case identInterval{}:
interval = option.Value().(time.Duration)
case identJitterFactor{}:
jitterFactor = option.Value().(float64)
case identRNG{}:
rng = option.Value().(Random)
}
}
return &ConstantInterval{
interval: interval,
jitter: newJitter(jitterFactor, rng),
}
}
func (g *ConstantInterval) Next() time.Duration {
return time.Duration(g.jitter.apply(float64(g.interval)))
}
type ConstantPolicy struct {
cOptions []ControllerOption
igOptions []ConstantOption
}
func NewConstantPolicy(options ...Option) *ConstantPolicy {
var cOptions []ControllerOption
var igOptions []ConstantOption
for _, option := range options {
switch opt := option.(type) {
case ControllerOption:
cOptions = append(cOptions, opt)
default:
igOptions = append(igOptions, opt.(ConstantOption))
}
}
return &ConstantPolicy{
cOptions: cOptions,
igOptions: igOptions,
}
}
func (p *ConstantPolicy) Start(ctx context.Context) Controller {
ig := NewConstantInterval(p.igOptions...)
return newController(ctx, ig, p.cOptions...)
}

View File

@ -1,99 +0,0 @@
package backoff
import (
"context"
"sync"
"time"
)
type controller struct {
ctx context.Context
cancel func()
ig IntervalGenerator
maxRetries int
mu *sync.RWMutex
next chan struct{} // user-facing channel
resetTimer chan time.Duration
retries int
timer *time.Timer
}
func newController(ctx context.Context, ig IntervalGenerator, options ...ControllerOption) *controller {
cctx, cancel := context.WithCancel(ctx) // DO NOT fire this cancel here
maxRetries := 10
for _, option := range options {
switch option.Ident() {
case identMaxRetries{}:
maxRetries = option.Value().(int)
}
}
c := &controller{
cancel: cancel,
ctx: cctx,
ig: ig,
maxRetries: maxRetries,
mu: &sync.RWMutex{},
next: make(chan struct{}, 1),
resetTimer: make(chan time.Duration, 1),
timer: time.NewTimer(ig.Next()),
}
// enqueue a single fake event so the user gets to retry once
c.next <- struct{}{}
go c.loop()
return c
}
func (c *controller) loop() {
for {
select {
case <-c.ctx.Done():
return
case d := <-c.resetTimer:
if !c.timer.Stop() {
select {
case <-c.timer.C:
default:
}
}
c.timer.Reset(d)
case <-c.timer.C:
select {
case <-c.ctx.Done():
return
case c.next <- struct{}{}:
}
if c.maxRetries > 0 {
c.retries++
}
if !c.check() {
c.cancel()
return
}
c.resetTimer <- c.ig.Next()
}
}
}
func (c *controller) check() bool {
if c.maxRetries > 0 && c.retries >= c.maxRetries {
return false
}
return true
}
func (c *controller) Done() <-chan struct{} {
c.mu.RLock()
defer c.mu.RUnlock()
return c.ctx.Done()
}
func (c *controller) Next() <-chan struct{} {
c.mu.RLock()
defer c.mu.RUnlock()
return c.next
}

View File

@ -1,6 +0,0 @@
// Package backoff implments backoff algorithms for retrying operations.
//
// Users first create an appropriate `Policy` object, and when the operation
// that needs retrying is about to start, they kick the actual backoff
//
package backoff

View File

@ -1,107 +0,0 @@
package backoff
import (
"context"
"time"
)
type ExponentialInterval struct {
current float64
maxInterval float64
minInterval float64
multiplier float64
jitter jitter
}
const (
defaultMaxInterval = float64(time.Minute)
defaultMinInterval = float64(500 * time.Millisecond)
defaultMultiplier = 1.5
)
func NewExponentialInterval(options ...ExponentialOption) *ExponentialInterval {
jitterFactor := 0.0
maxInterval := defaultMaxInterval
minInterval := defaultMinInterval
multiplier := defaultMultiplier
var rng Random
for _, option := range options {
switch option.Ident() {
case identJitterFactor{}:
jitterFactor = option.Value().(float64)
case identMaxInterval{}:
maxInterval = float64(option.Value().(time.Duration))
case identMinInterval{}:
minInterval = float64(option.Value().(time.Duration))
case identMultiplier{}:
multiplier = option.Value().(float64)
case identRNG{}:
rng = option.Value().(Random)
}
}
if minInterval > maxInterval {
minInterval = maxInterval
}
if multiplier <= 1 {
multiplier = defaultMultiplier
}
return &ExponentialInterval{
maxInterval: maxInterval,
minInterval: minInterval,
multiplier: multiplier,
jitter: newJitter(jitterFactor, rng),
}
}
func (g *ExponentialInterval) Next() time.Duration {
var next float64
if g.current == 0 {
next = g.minInterval
} else {
next = g.current * g.multiplier
}
if next > g.maxInterval {
next = g.maxInterval
}
if next < g.minInterval {
next = g.minInterval
}
// Apply jitter *AFTER* we calculate the base interval
next = g.jitter.apply(next)
g.current = next
return time.Duration(next)
}
type ExponentialPolicy struct {
cOptions []ControllerOption
igOptions []ExponentialOption
}
func NewExponentialPolicy(options ...ExponentialOption) *ExponentialPolicy {
var cOptions []ControllerOption
var igOptions []ExponentialOption
for _, option := range options {
switch opt := option.(type) {
case ControllerOption:
cOptions = append(cOptions, opt)
default:
igOptions = append(igOptions, opt)
}
}
return &ExponentialPolicy{
cOptions: cOptions,
igOptions: igOptions,
}
}
func (p *ExponentialPolicy) Start(ctx context.Context) Controller {
ig := NewExponentialInterval(p.igOptions...)
return newController(ctx, ig, p.cOptions...)
}

View File

@ -1,30 +0,0 @@
package backoff
import (
"context"
"time"
"github.com/lestrrat-go/option"
)
type Option = option.Interface
type Controller interface {
Done() <-chan struct{}
Next() <-chan struct{}
}
type IntervalGenerator interface {
Next() time.Duration
}
// Policy is an interface for the backoff policies that this package
// implements. Users must create a controller object from this
// policy to actually do anything with it
type Policy interface {
Start(context.Context) Controller
}
type Random interface {
Float64() float64
}

View File

@ -1,59 +0,0 @@
package backoff
import (
"math/rand"
"time"
)
type jitter interface {
apply(interval float64) float64
}
func newJitter(jitterFactor float64, rng Random) jitter {
if jitterFactor <= 0 || jitterFactor >= 1 {
return newNopJitter()
}
return newRandomJitter(jitterFactor, rng)
}
type nopJitter struct{}
func newNopJitter() *nopJitter {
return &nopJitter{}
}
func (j *nopJitter) apply(interval float64) float64 {
return interval
}
type randomJitter struct {
jitterFactor float64
rng Random
}
func newRandomJitter(jitterFactor float64, rng Random) *randomJitter {
if rng == nil {
// if we have a jitter factor, and no RNG is provided, create one.
// This is definitely not "secure", but well, if you care enough,
// you would provide one
rng = rand.New(rand.NewSource(time.Now().UnixNano()))
}
return &randomJitter{
jitterFactor: jitterFactor,
rng: rng,
}
}
func (j *randomJitter) apply(interval float64) float64 {
jitterDelta := interval * j.jitterFactor
jitterMin := interval - jitterDelta
jitterMax := interval + jitterDelta
// Get a random value from the range [minInterval, maxInterval].
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
// we want a 33% chance for selecting either 1, 2 or 3.
//
// see also: https://github.com/cenkalti/backoff/blob/c2975ffa541a1caeca5f76c396cb8c3e7b3bb5f8/exponential.go#L154-L157
return jitterMin + j.rng.Float64()*(jitterMax-jitterMin+1)
}

View File

@ -1,51 +0,0 @@
package backoff
import (
"context"
"sync"
)
// NullPolicy does not do any backoff. It allows the caller
// to execute the desired code once, and no more
type NullPolicy struct{}
func NewNull() *NullPolicy {
return &NullPolicy{}
}
func (p *NullPolicy) Start(ctx context.Context) Controller {
return newNullController(ctx)
}
type nullController struct {
mu *sync.RWMutex
ctx context.Context
next chan struct{}
}
func newNullController(ctx context.Context) *nullController {
cctx, cancel := context.WithCancel(ctx)
c := &nullController{
mu: &sync.RWMutex{},
ctx: cctx,
next: make(chan struct{}), // NO BUFFER
}
go func(ch chan struct{}, cancel func()) {
ch <- struct{}{}
close(ch)
cancel()
}(c.next, cancel)
return c
}
func (c *nullController) Done() <-chan struct{} {
c.mu.RLock()
defer c.mu.RUnlock()
return c.ctx.Done()
}
func (c *nullController) Next() <-chan struct{} {
c.mu.RLock()
defer c.mu.RUnlock()
return c.next
}

View File

@ -1,127 +0,0 @@
package backoff
import (
"time"
"github.com/lestrrat-go/option"
)
type identInterval struct{}
type identJitterFactor struct{}
type identMaxInterval struct{}
type identMaxRetries struct{}
type identMinInterval struct{}
type identMultiplier struct{}
type identRNG struct{}
// ControllerOption is an option that may be passed to Policy objects,
// but are ultimately passed down to the Controller objects.
// (Normally you do not have to care about the distinction)
type ControllerOption interface {
ConstantOption
ExponentialOption
CommonOption
controllerOption()
}
type controllerOption struct {
Option
}
func (*controllerOption) exponentialOption() {}
func (*controllerOption) controllerOption() {}
func (*controllerOption) constantOption() {}
// ConstantOption is an option that is used by the Constant policy.
type ConstantOption interface {
Option
constantOption()
}
type constantOption struct {
Option
}
func (*constantOption) constantOption() {}
// ExponentialOption is an option that is used by the Exponential policy.
type ExponentialOption interface {
Option
exponentialOption()
}
type exponentialOption struct {
Option
}
func (*exponentialOption) exponentialOption() {}
// CommonOption is an option that can be passed to any of the backoff policies.
type CommonOption interface {
ExponentialOption
ConstantOption
}
type commonOption struct {
Option
}
func (*commonOption) constantOption() {}
func (*commonOption) exponentialOption() {}
// WithMaxRetries specifies the maximum number of attempts that can be made
// by the backoff policies. By default each policy tries up to 10 times.
//
// If you would like to retry forever, specify "0" and pass to the constructor
// of each policy.
//
// This option can be passed to all policy constructors except for NullPolicy
func WithMaxRetries(v int) ControllerOption {
return &controllerOption{option.New(identMaxRetries{}, v)}
}
// WithInterval specifies the constant interval used in ConstantPolicy and
// ConstantInterval.
// The default value is 1 minute.
func WithInterval(v time.Duration) ConstantOption {
return &constantOption{option.New(identInterval{}, v)}
}
// WithMaxInterval specifies the maximum duration used in exponential backoff
// The default value is 1 minute.
func WithMaxInterval(v time.Duration) ExponentialOption {
return &exponentialOption{option.New(identMaxInterval{}, v)}
}
// WithMinInterval specifies the minimum duration used in exponential backoff.
// The default value is 500ms.
func WithMinInterval(v time.Duration) ExponentialOption {
return &exponentialOption{option.New(identMinInterval{}, v)}
}
// WithMultiplier specifies the factor in which the backoff intervals are
// increased. By default this value is set to 1.5, which means that for
// every iteration a 50% increase in the interval for every iteration
// (up to the value specified by WithMaxInterval). this value must be greater
// than 1.0. If the value is less than equal to 1.0, the default value
// of 1.5 is used.
func WithMultiplier(v float64) ExponentialOption {
return &exponentialOption{option.New(identMultiplier{}, v)}
}
// WithJitterFactor enables some randomness (jittering) in the computation of
// the backoff intervals. This value must be between 0.0 < v < 1.0. If a
// value outside of this range is specified, the value will be silently
// ignored and jittering is disabled.
//
// This option can be passed to ExponentialPolicy or ConstantPolicy constructor
func WithJitterFactor(v float64) CommonOption {
return &commonOption{option.New(identJitterFactor{}, v)}
}
// WithRNG specifies the random number generator used for jittering.
// If not provided one will be created, but if you want a truly random
// jittering, make sure to provide one that you explicitly initialized
func WithRNG(v Random) CommonOption {
return &commonOption{option.New(identRNG{}, v)}
}

View File

@ -1,14 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
# Dependency directories (remove the comment below to include it)
# vendor/

82
vendor/github.com/lestrrat-go/httprc/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,82 @@
run:
linters-settings:
govet:
enable-all: true
disable:
- shadow
- fieldalignment
linters:
enable-all: true
disable:
- cyclop
- dupl
- exhaustive
- exhaustivestruct
- errorlint
- funlen
- gci
- gochecknoglobals
- gochecknoinits
- gocognit
- gocritic
- gocyclo
- godot
- godox
- goerr113
- gofumpt
- golint #deprecated
- gomnd
- gosec
- govet
- interfacer # deprecated
- ifshort
- lll
- maligned # deprecated
- makezero
- nakedret
- nestif
- nlreturn
- paralleltest
- scopelint # deprecated
- tagliatelle
- testpackage
- thelper
- wrapcheck
- wsl
issues:
exclude-rules:
# not needed
- path: /*.go
text: "ST1003: should not use underscores in package names"
linters:
- stylecheck
- path: /*.go
text: "don't use an underscore in package name"
linters:
- revive
- path: /main.go
linters:
- errcheck
- path: internal/codegen/codegen.go
linters:
- errcheck
- path: /*_test.go
linters:
- errcheck
- forcetypeassert
- path: /*_example_test.go
linters:
- forbidigo
- path: cmd/jwx/jwx.go
linters:
- forbidigo
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-issues-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0

8
vendor/github.com/lestrrat-go/httprc/Changes generated vendored Normal file
View File

@ -0,0 +1,8 @@
Changes
=======
v1.0.1 29 Mar 2022
* Bump dependency for github.com/lestrrat-go/httpcc to v1.0.1
v1.0.0 29 Mar 2022
* Initial release, refactored out of github.com/lestrrat-go/jwx

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018 lestrrat
Copyright (c) 2022 lestrrat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

130
vendor/github.com/lestrrat-go/httprc/README.md generated vendored Normal file
View File

@ -0,0 +1,130 @@
# httprc
`httprc` is a HTTP "Refresh" Cache. Its aim is to cache a remote resource that
can be fetched via HTTP, but keep the cached content up-to-date based on periodic
refreshing.
# SYNOPSIS
<!-- INCLUDE(httprc_example_test.go) -->
```go
package httprc_test
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"sync"
"time"
"github.com/lestrrat-go/httprc"
)
const (
helloWorld = `Hello World!`
goodbyeWorld = `Goodbye World!`
)
func ExampleCache() {
var mu sync.RWMutex
msg := helloWorld
srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(`Cache-Control`, fmt.Sprintf(`max-age=%d`, 2))
w.WriteHeader(http.StatusOK)
mu.RLock()
fmt.Fprint(w, msg)
mu.RUnlock()
}))
defer srv.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
errSink := httprc.ErrSinkFunc(func(err error) {
fmt.Printf("%s\n", err)
})
c := httprc.NewCache(ctx,
httprc.WithErrSink(errSink),
httprc.WithRefreshWindow(time.Second), // force checks every second
)
c.Register(srv.URL,
httprc.WithHTTPClient(srv.Client()), // we need client with TLS settings
httprc.WithMinRefreshInterval(time.Second), // allow max-age=1 (smallest)
)
payload, err := c.Get(ctx, srv.URL)
if err != nil {
fmt.Printf("%s\n", err)
return
}
if string(payload.([]byte)) != helloWorld {
fmt.Printf("payload mismatch: %s\n", payload)
return
}
mu.Lock()
msg = goodbyeWorld
mu.Unlock()
time.Sleep(4 * time.Second)
payload, err = c.Get(ctx, srv.URL)
if err != nil {
fmt.Printf("%s\n", err)
return
}
if string(payload.([]byte)) != goodbyeWorld {
fmt.Printf("payload mismatch: %s\n", payload)
return
}
cancel()
// OUTPUT:
}
```
source: [httprc_example_test.go](https://github.com/lestrrat-go/jwx/blob/main/httprc_example_test.go)
<!-- END INCLUDE -->
# Sequence Diagram
```mermaid
sequenceDiagram
autonumber
actor User
participant httprc.Cache
participant httprc.Storage
User->>httprc.Cache: Fetch URL `u`
activate httprc.Storage
httprc.Cache->>httprc.Storage: Fetch local cache for `u`
alt Cache exists
httprc.Storage-->httprc.Cache: Return local cache
httprc.Cache-->>User: Return data
Note over httprc.Storage: If the cache exists, there's nothing more to do.<br />The cached content will be updated periodically in httprc.Refresher
deactivate httprc.Storage
else Cache does not exist
activate httprc.Fetcher
httprc.Cache->>httprc.Fetcher: Fetch remote resource `u`
httprc.Fetcher-->>httprc.Cache: Return fetched data
deactivate httprc.Fetcher
httprc.Cache-->>User: Return data
httprc.Cache-)httprc.Refresher: Enqueue into auto-refresh queue
activate httprc.Refresher
loop Refresh Loop
Note over httprc.Storage,httprc.Fetcher: Cached contents are updated synchronously
httprc.Refresher->>httprc.Refresher: Wait until next refresh
httprc.Refresher-->>httprc.Fetcher: Request fetch
httprc.Fetcher->>httprc.Refresher: Return fetched data
httprc.Refresher-->>httprc.Storage: Store new version in cache
httprc.Refresher->>httprc.Refresher: Enqueue into auto-refresh queue (again)
end
deactivate httprc.Refresher
end
```

171
vendor/github.com/lestrrat-go/httprc/cache.go generated vendored Normal file
View File

@ -0,0 +1,171 @@
package httprc
import (
"context"
"fmt"
"net/http"
"sync"
"time"
)
// ErrSink is an abstraction that allows users to consume errors
// produced while the cache queue is running.
type HTTPClient interface {
Get(string) (*http.Response, error)
}
// Cache represents a cache that stores resources locally, while
// periodically refreshing the contents based on HTTP header values
// and/or user-supplied hints.
//
// Refresh is performed _periodically_, and therefore the contents
// are not kept up-to-date in real time. The interval between checks
// for refreshes is called the refresh window.
//
// The default refresh window is 15 minutes. This means that if a
// resource is fetched is at time T, and it is supposed to be
// refreshed in 20 minutes, the next refresh for this resource will
// happen at T+30 minutes (15+15 minutes).
type Cache struct {
mu sync.RWMutex
queue *queue
wl Whitelist
}
const defaultRefreshWindow = 15 * time.Minute
// New creates a new Cache object.
//
// The context object in the argument controls the life-cycle of the
// auto-refresh worker. If you cancel the `ctx`, then the automatic
// refresh will stop working.
//
// Refresh will only be performed periodically where the interval between
// refreshes are controlled by the `refresh window` variable. For example,
// if the refresh window is every 5 minutes and the resource was queued
// to be refreshed at 7 minutes, the resource will be refreshed after 10
// minutes (in 2 refresh window time).
//
// The refresh window can be configured by using `httprc.WithRefreshWindow`
// option. If you want refreshes to be performed more often, provide a smaller
// refresh window. If you specify a refresh window that is smaller than 1
// second, it will automatically be set to the default value, which is 15
// minutes.
//
// Internally the HTTP fetching is done using a pool of HTTP fetch
// workers. The default number of workers is 3. You may change this
// number by specifying the `httprc.WithFetcherWorkerCount`
func NewCache(ctx context.Context, options ...CacheOption) *Cache {
var refreshWindow time.Duration
var errSink ErrSink
var wl Whitelist
var fetcherOptions []FetcherOption
for _, option := range options {
//nolint:forcetypeassert
switch option.Ident() {
case identRefreshWindow{}:
refreshWindow = option.Value().(time.Duration)
case identFetcherWorkerCount{}, identWhitelist{}:
fetcherOptions = append(fetcherOptions, option)
case identErrSink{}:
errSink = option.Value().(ErrSink)
}
}
if refreshWindow < time.Second {
refreshWindow = defaultRefreshWindow
}
fetch := NewFetcher(ctx, fetcherOptions...)
queue := newQueue(ctx, refreshWindow, fetch, errSink)
return &Cache{
queue: queue,
wl: wl,
}
}
// Register configures a URL to be stored in the cache.
//
// For any given URL, the URL must be registered _BEFORE_ it is
// accessed using `Get()` method.
func (c *Cache) Register(u string, options ...RegisterOption) error {
c.mu.Lock()
defer c.mu.Unlock()
if wl := c.wl; wl != nil {
if !wl.IsAllowed(u) {
return fmt.Errorf(`httprc.Cache: url %q has been rejected by whitelist`, u)
}
}
return c.queue.Register(u, options...)
}
// Unregister removes the given URL `u` from the cache.
//
// Subsequent calls to `Get()` will fail until `u` is registered again.
func (c *Cache) Unregister(u string) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.queue.Unregister(u)
}
// IsRegistered returns true if the given URL `u` has already been
// registered in the cache.
func (c *Cache) IsRegistered(u string) bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.queue.IsRegistered(u)
}
// Refresh is identical to Get(), except it always fetches the
// specified resource anew, and updates the cached content
func (c *Cache) Refresh(ctx context.Context, u string) (interface{}, error) {
return c.getOrFetch(ctx, u, true)
}
// Get returns the cached object.
//
// The context.Context argument is used to control the timeout for
// synchronous fetches, when they need to happen. Synchronous fetches
// will be performed when the cache does not contain the specified
// resource.
func (c *Cache) Get(ctx context.Context, u string) (interface{}, error) {
return c.getOrFetch(ctx, u, false)
}
func (c *Cache) getOrFetch(ctx context.Context, u string, forceRefresh bool) (interface{}, error) {
c.mu.RLock()
e, ok := c.queue.getRegistered(u)
if !ok {
c.mu.RUnlock()
return nil, fmt.Errorf(`url %q is not registered (did you make sure to call Register() first?)`, u)
}
c.mu.RUnlock()
// Only one goroutine may enter this section.
e.acquireSem()
// has this entry been fetched? (but ignore and do a fetch
// if forceRefresh is true)
if forceRefresh || !e.hasBeenFetched() {
if err := c.queue.fetchAndStore(ctx, e); err != nil {