mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-15 04:18:03 +00:00
Add redis support for distributed caching (#83)
* Add redis support for distributed caching * Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * fixup! fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi. * ... and another all nighter. * fixup! ... and another all nighter. * fixup! fixup! ... and another all nighter. * fixup! fixup! fixup! ... and another all nighter. * Resolve issue #85 by adding ability to set custom claims in JWT tokens * Remove redundant validation in auth middleware ( issue #89 ) * Add ability to set cookie prefix for session cookies ( #87 ) * fixup! Add ability to set cookie prefix for session cookies ( #87 ) * Add ability to set cookie max age - issue #91 * Potential fix for code scanning alert no. 10: Size computation for allocation may overflow Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fixup! Merge main into 0.8.0-redis: resolve conflicts --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
parent
5fcbd54955
commit
e64fc7f730
+264
@@ -0,0 +1,264 @@
|
||||
package miniredis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis/v2/server"
|
||||
)
|
||||
|
||||
const (
|
||||
keyTypeString = "string"
|
||||
keyTypeHash = "hash"
|
||||
keyTypeList = "list"
|
||||
keyTypeSet = "set"
|
||||
keyTypeHll = "hll"
|
||||
keyTypeSortedSet = "zset"
|
||||
keyTypeStream = "stream"
|
||||
)
|
||||
|
||||
const (
|
||||
msgWrongType = "WRONGTYPE Operation against a key holding the wrong kind of value"
|
||||
msgNotValidHllValue = "WRONGTYPE Key is not a valid HyperLogLog string value."
|
||||
msgInvalidInt = "ERR value is not an integer or out of range"
|
||||
msgIntOverflow = "ERR increment or decrement would overflow"
|
||||
msgInvalidFloat = "ERR value is not a valid float"
|
||||
msgInvalidMinMax = "ERR min or max is not a float"
|
||||
msgInvalidRangeItem = "ERR min or max not valid string range item"
|
||||
msgInvalidTimeout = "ERR timeout is not a float or out of range"
|
||||
msgInvalidRange = "ERR value is out of range, must be positive"
|
||||
msgSyntaxError = "ERR syntax error"
|
||||
msgKeyNotFound = "ERR no such key"
|
||||
msgOutOfRange = "ERR index out of range"
|
||||
msgInvalidCursor = "ERR invalid cursor"
|
||||
msgXXandNX = "ERR XX and NX options at the same time are not compatible"
|
||||
msgTimeoutNegative = "ERR timeout is negative"
|
||||
msgTimeoutIsOutOfRange = "ERR timeout is out of range"
|
||||
msgInvalidSETime = "ERR invalid expire time in set"
|
||||
msgInvalidSETEXTime = "ERR invalid expire time in setex"
|
||||
msgInvalidPSETEXTime = "ERR invalid expire time in psetex"
|
||||
msgInvalidKeysNumber = "ERR Number of keys can't be greater than number of args"
|
||||
msgNegativeKeysNumber = "ERR Number of keys can't be negative"
|
||||
msgFScriptUsage = "ERR unknown subcommand or wrong number of arguments for '%s'. Try SCRIPT HELP."
|
||||
msgFScriptUsageSimple = "ERR unknown subcommand '%s'. Try SCRIPT HELP."
|
||||
msgFPubsubUsage = "ERR unknown subcommand or wrong number of arguments for '%s'. Try PUBSUB HELP."
|
||||
msgFPubsubUsageSimple = "ERR unknown subcommand '%s'. Try PUBSUB HELP."
|
||||
msgFObjectUsage = "ERR unknown subcommand '%s'. Try OBJECT HELP."
|
||||
msgScriptFlush = "ERR SCRIPT FLUSH only support SYNC|ASYNC option"
|
||||
msgSingleElementPair = "ERR INCR option supports a single increment-element pair"
|
||||
msgGTLTandNX = "ERR GT, LT, and/or NX options at the same time are not compatible"
|
||||
msgInvalidStreamID = "ERR Invalid stream ID specified as stream command argument"
|
||||
msgStreamIDTooSmall = "ERR The ID specified in XADD is equal or smaller than the target stream top item"
|
||||
msgStreamIDZero = "ERR The ID specified in XADD must be greater than 0-0"
|
||||
msgNoScriptFound = "NOSCRIPT No matching script. Please use EVAL."
|
||||
msgUnsupportedUnit = "ERR unsupported unit provided. please use M, KM, FT, MI"
|
||||
msgXreadUnbalanced = "ERR Unbalanced 'xread' list of streams: for each stream key an ID or '$' must be specified."
|
||||
msgXgroupKeyNotFound = "ERR The XGROUP subcommand requires the key to exist. Note that for CREATE you may want to use the MKSTREAM option to create an empty stream automatically."
|
||||
msgXtrimInvalidStrategy = "ERR unsupported XTRIM strategy. Please use MAXLEN, MINID"
|
||||
msgXtrimInvalidMaxLen = "ERR value is not an integer or out of range"
|
||||
msgXtrimInvalidLimit = "ERR syntax error, LIMIT cannot be used without the special ~ option"
|
||||
msgDBIndexOutOfRange = "ERR DB index is out of range"
|
||||
msgLimitCombination = "ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX"
|
||||
msgRankIsZero = "ERR RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start from the end of the list"
|
||||
msgCountIsNegative = "ERR COUNT can't be negative"
|
||||
msgMaxLengthIsNegative = "ERR MAXLEN can't be negative"
|
||||
msgLimitIsNegative = "ERR LIMIT can't be negative"
|
||||
msgMemorySubcommand = "ERR unknown subcommand '%s'. Try MEMORY HELP."
|
||||
)
|
||||
|
||||
func errWrongNumber(cmd string) string {
|
||||
return fmt.Sprintf("ERR wrong number of arguments for '%s' command", strings.ToLower(cmd))
|
||||
}
|
||||
|
||||
func errLuaParseError(err error) string {
|
||||
return fmt.Sprintf("ERR Error compiling script (new function): %s", err.Error())
|
||||
}
|
||||
|
||||
func errReadgroup(key, group string) error {
|
||||
return fmt.Errorf("NOGROUP No such key '%s' or consumer group '%s'", key, group)
|
||||
}
|
||||
|
||||
func errXreadgroup(key, group string) error {
|
||||
return fmt.Errorf("NOGROUP No such key '%s' or consumer group '%s' in XREADGROUP with GROUP option", key, group)
|
||||
}
|
||||
|
||||
func msgNotFromScripts(sha string) string {
|
||||
return fmt.Sprintf("This Redis command is not allowed from script script: %s, &c", sha)
|
||||
}
|
||||
|
||||
// withTx wraps the non-argument-checking part of command handling code in
|
||||
// transaction logic.
|
||||
func withTx(
|
||||
m *Miniredis,
|
||||
c *server.Peer,
|
||||
cb txCmd,
|
||||
) {
|
||||
ctx := getCtx(c)
|
||||
|
||||
if ctx.nested {
|
||||
// this is a call via Lua's .call(). It's already locked.
|
||||
cb(c, ctx)
|
||||
m.signal.Broadcast()
|
||||
return
|
||||
}
|
||||
|
||||
if inTx(ctx) {
|
||||
addTxCmd(ctx, cb)
|
||||
c.WriteInline("QUEUED")
|
||||
return
|
||||
}
|
||||
m.Lock()
|
||||
cb(c, ctx)
|
||||
// done, wake up anyone who waits on anything.
|
||||
m.signal.Broadcast()
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
// blockCmd is executed returns whether it is done
|
||||
type blockCmd func(*server.Peer, *connCtx) bool
|
||||
|
||||
// blocking keeps trying a command until the callback returns true. Calls
|
||||
// onTimeout after the timeout (or when we call this in a transaction).
|
||||
func blocking(
|
||||
m *Miniredis,
|
||||
c *server.Peer,
|
||||
timeout time.Duration,
|
||||
cb blockCmd,
|
||||
onTimeout func(*server.Peer),
|
||||
) {
|
||||
var (
|
||||
ctx = getCtx(c)
|
||||
)
|
||||
if inTx(ctx) {
|
||||
addTxCmd(ctx, func(c *server.Peer, ctx *connCtx) {
|
||||
if !cb(c, ctx) {
|
||||
onTimeout(c)
|
||||
}
|
||||
})
|
||||
c.WriteInline("QUEUED")
|
||||
return
|
||||
}
|
||||
|
||||
localCtx, cancel := context.WithCancel(m.Ctx)
|
||||
defer cancel()
|
||||
timedOut := false
|
||||
if timeout != 0 {
|
||||
go setCondTimer(localCtx, m.signal, &timedOut, timeout)
|
||||
}
|
||||
go func() {
|
||||
<-localCtx.Done()
|
||||
m.signal.Broadcast() // main loop might miss this signal
|
||||
}()
|
||||
|
||||
if !ctx.nested {
|
||||
// this is a call via Lua's .call(). It's already locked.
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
}
|
||||
for {
|
||||
if c.Closed() {
|
||||
return
|
||||
}
|
||||
|
||||
if m.Ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
done := cb(c, ctx)
|
||||
if done {
|
||||
return
|
||||
}
|
||||
|
||||
if timedOut {
|
||||
onTimeout(c)
|
||||
return
|
||||
}
|
||||
|
||||
m.signal.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func setCondTimer(ctx context.Context, sig *sync.Cond, timedOut *bool, timeout time.Duration) {
|
||||
dl := time.NewTimer(timeout)
|
||||
defer dl.Stop()
|
||||
select {
|
||||
case <-dl.C:
|
||||
sig.L.Lock() // for timedOut
|
||||
*timedOut = true
|
||||
sig.Broadcast() // main loop might miss this signal
|
||||
sig.L.Unlock()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
// formatBig formats a float the way redis does
|
||||
func formatBig(v *big.Float) string {
|
||||
// Format with %f and strip trailing 0s.
|
||||
if v.IsInf() {
|
||||
return "inf"
|
||||
}
|
||||
// if math.IsInf(v, -1) {
|
||||
// return "-inf"
|
||||
// }
|
||||
return stripZeros(fmt.Sprintf("%.17f", v))
|
||||
}
|
||||
|
||||
func stripZeros(sv string) string {
|
||||
for strings.Contains(sv, ".") {
|
||||
if sv[len(sv)-1] != '0' {
|
||||
break
|
||||
}
|
||||
// Remove trailing 0s.
|
||||
sv = sv[:len(sv)-1]
|
||||
// Ends with a '.'.
|
||||
if sv[len(sv)-1] == '.' {
|
||||
sv = sv[:len(sv)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
return sv
|
||||
}
|
||||
|
||||
// redisRange gives Go offsets for something l long with start/end in
|
||||
// Redis semantics. Both start and end can be negative.
|
||||
// Used for string range and list range things.
|
||||
// The results can be used as: v[start:end]
|
||||
// Note that GETRANGE (on a string key) never returns an empty string when end
|
||||
// is a large negative number.
|
||||
func redisRange(l, start, end int, stringSymantics bool) (int, int) {
|
||||
if start < 0 {
|
||||
start = l + start
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
}
|
||||
if start > l {
|
||||
start = l
|
||||
}
|
||||
|
||||
if end < 0 {
|
||||
end = l + end
|
||||
if end < 0 {
|
||||
end = -1
|
||||
if stringSymantics {
|
||||
end = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
if end < math.MaxInt32 {
|
||||
end++ // end argument is inclusive in Redis.
|
||||
}
|
||||
if end > l {
|
||||
end = l
|
||||
}
|
||||
|
||||
if end < start {
|
||||
return 0, 0
|
||||
}
|
||||
return start, end
|
||||
}
|
||||
Reference in New Issue
Block a user