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:
2025-11-30 02:18:46 +00:00
committed by GitHub
co-authored by Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
parent 5fcbd54955
commit e64fc7f730
318 changed files with 100989 additions and 948 deletions
+26
View File
@@ -0,0 +1,26 @@
This code is derived from the C code in redis-7.2.0/deps/fpconv/*, which has
this license:
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+6
View File
@@ -0,0 +1,6 @@
.PHONY: test fuzz
test:
go test
fuzz:
go test -fuzz=Fuzz
+3
View File
@@ -0,0 +1,3 @@
This is a translation of the actual C code in Redis (7.2) which does the float
-> string conversion.
Strconv does a close enough job, but we can use the exact same logic, so why not.
+286
View File
@@ -0,0 +1,286 @@
package fpconv
import (
"math"
)
var (
fracmask uint64 = 0x000FFFFFFFFFFFFF
expmask uint64 = 0x7FF0000000000000
hiddenbit uint64 = 0x0010000000000000
signmask uint64 = 0x8000000000000000
expbias int64 = 1023 + 52
zeros = []rune("0000000000000000000000")
tens = []uint64{
10000000000000000000,
1000000000000000000,
100000000000000000,
10000000000000000,
1000000000000000,
100000000000000,
10000000000000,
1000000000000,
100000000000,
10000000000,
1000000000,
100000000,
10000000,
1000000,
100000,
10000,
1000,
100,
10,
1}
)
func absv(n int) int {
if n < 0 {
return -n
}
return n
}
func minv(a, b int) int {
if a < b {
return a
}
return b
}
func Dtoa(d float64) string {
var (
dest [25]rune // Note C has 24, which is broken
digits [18]rune
str_len int = 0
neg = false
)
if get_dbits(d)&signmask != 0 {
dest[0] = '-'
str_len++
neg = true
}
if spec := filter_special(d, dest[str_len:]); spec != 0 {
return string(dest[:str_len+spec])
}
var (
k int = 0
ndigits int = grisu2(d, &digits, &k)
)
str_len += emit_digits(&digits, ndigits, dest[str_len:], k, neg)
return string(dest[:str_len])
}
func filter_special(fp float64, dest []rune) int {
if fp == 0.0 {
dest[0] = '0'
return 1
}
if math.IsNaN(fp) {
dest[0] = 'n'
dest[1] = 'a'
dest[2] = 'n'
return 3
}
if math.IsInf(fp, 0) {
dest[0] = 'i'
dest[1] = 'n'
dest[2] = 'f'
return 3
}
return 0
}
func grisu2(d float64, digits *[18]rune, K *int) int {
w := build_fp(d)
lower, upper := get_normalized_boundaries(w)
w = normalize(w)
var k int64
cp := find_cachedpow10(upper.exp, &k)
w = multiply(w, cp)
upper = multiply(upper, cp)
lower = multiply(lower, cp)
lower.frac++
upper.frac--
*K = int(-k)
return generate_digits(w, upper, lower, digits[:], K)
}
func emit_digits(digits *[18]rune, ndigits int, dest []rune, K int, neg bool) int {
exp := int(absv(K + ndigits - 1))
/* write plain integer */
if K >= 0 && (exp < (ndigits + 7)) {
copy(dest, digits[:ndigits])
copy(dest[ndigits:], zeros[:K])
return ndigits + K
}
/* write decimal w/o scientific notation */
if K < 0 && (K > -7 || exp < 4) {
offset := int(ndigits - absv(K))
/* fp < 1.0 -> write leading zero */
if offset <= 0 {
offset = -offset
dest[0] = '0'
dest[1] = '.'
copy(dest[2:], zeros[:offset])
copy(dest[offset+2:], digits[:ndigits])
return ndigits + 2 + offset
/* fp > 1.0 */
} else {
copy(dest, digits[:offset])
dest[offset] = '.'
copy(dest[offset+1:], digits[offset:offset+ndigits-offset])
return ndigits + 1
}
}
/* write decimal w/ scientific notation */
l := 18 // was: 18-neg
if neg {
l--
}
ndigits = minv(ndigits, l)
var idx int = 0
dest[idx] = digits[0]
idx++
if ndigits > 1 {
dest[idx] = '.'
idx++
copy(dest[idx:], digits[+1:ndigits-1+1])
idx += ndigits - 1
}
dest[idx] = 'e'
idx++
sign := '+'
if K+ndigits-1 < 0 {
sign = '-'
}
dest[idx] = sign
idx++
var cent rune = 0
if exp > 99 {
cent = rune(exp / 100)
dest[idx] = cent + '0'
idx++
exp -= int(cent) * 100
}
if exp > 9 {
dec := rune(exp / 10)
dest[idx] = dec + '0'
idx++
exp -= int(dec) * 10
} else if cent != 0 {
dest[idx] = '0'
idx++
}
dest[idx] = rune(exp%10) + '0'
idx++
return idx
}
func generate_digits(fp, upper, lower Fp, digits []rune, K *int) int {
var (
wfrac = uint64(upper.frac - fp.frac)
delta = uint64(upper.frac - lower.frac)
)
one := Fp{
frac: 1 << -upper.exp,
exp: upper.exp,
}
part1 := uint64(upper.frac >> -one.exp)
part2 := uint64(upper.frac & (one.frac - 1))
var (
idx = 0
kappa = 10
index = 10
)
/* 1000000000 */
for ; kappa > 0; index++ {
div := tens[index]
digit := part1 / div
if digit != 0 || idx != 0 {
digits[idx] = rune(digit) + '0'
idx++
}
part1 -= digit * div
kappa--
tmp := (part1 << -one.exp) + part2
if tmp <= delta {
*K += kappa
round_digit(digits, idx, delta, tmp, div<<-one.exp, wfrac)
return idx
}
}
/* 10 */
index = 18
for {
var unit uint64 = tens[index]
part2 *= 10
delta *= 10
kappa--
digit := part2 >> -one.exp
if digit != 0 || idx != 0 {
digits[idx] = rune(digit) + '0'
idx++
}
part2 &= uint64(one.frac) - 1
if part2 < delta {
*K += kappa
round_digit(digits, idx, delta, part2, uint64(one.frac), wfrac*unit)
return idx
}
index--
}
}
func round_digit(digits []rune,
ndigits int,
delta uint64,
rem uint64,
kappa uint64,
frac uint64) {
for rem < frac && delta-rem >= kappa &&
(rem+kappa < frac || frac-rem > rem+kappa-frac) {
digits[ndigits-1]--
rem += kappa
}
}
+96
View File
@@ -0,0 +1,96 @@
package fpconv
import (
"math"
)
type (
Fp struct {
frac uint64
exp int64
}
)
func build_fp(d float64) Fp {
bits := get_dbits(d)
fp := Fp{
frac: bits & fracmask,
exp: int64((bits & expmask) >> 52),
}
if fp.exp != 0 {
fp.frac += hiddenbit
fp.exp -= expbias
} else {
fp.exp = -expbias + 1
}
return fp
}
func normalize(fp Fp) Fp {
for (fp.frac & hiddenbit) == 0 {
fp.frac <<= 1
fp.exp--
}
var shift int64 = 64 - 52 - 1
fp.frac <<= shift
fp.exp -= shift
return fp
}
func multiply(a, b Fp) Fp {
lomask := uint64(0x00000000FFFFFFFF)
var (
ah_bl = uint64((a.frac >> 32) * (b.frac & lomask))
al_bh = uint64((a.frac & lomask) * (b.frac >> 32))
al_bl = uint64((a.frac & lomask) * (b.frac & lomask))
ah_bh = uint64((a.frac >> 32) * (b.frac >> 32))
)
tmp := uint64((ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32))
/* round up */
tmp += uint64(1) << 31
return Fp{
ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32),
a.exp + b.exp + 64,
}
}
func get_dbits(d float64) uint64 {
return math.Float64bits(d)
}
func get_normalized_boundaries(fp Fp) (Fp, Fp) {
upper := Fp{
frac: (fp.frac << 1) + 1,
exp: fp.exp - 1,
}
for (upper.frac & (hiddenbit << 1)) == 0 {
upper.frac <<= 1
upper.exp--
}
var u_shift int64 = 64 - 52 - 2
upper.frac <<= u_shift
upper.exp = upper.exp - u_shift
l_shift := int64(1)
if fp.frac == hiddenbit {
l_shift = 2
}
lower := Fp{
frac: (fp.frac << l_shift) - 1,
exp: fp.exp - l_shift,
}
lower.frac <<= lower.exp - upper.exp
lower.exp = upper.exp
return lower, upper
}
+82
View File
@@ -0,0 +1,82 @@
package fpconv
var (
npowers int64 = 87
steppowers int64 = 8
firstpower int64 = -348 /* 10 ^ -348 */
expmax = -32
expmin = -60
powers_ten = []Fp{
{18054884314459144840, -1220}, {13451937075301367670, -1193},
{10022474136428063862, -1166}, {14934650266808366570, -1140},
{11127181549972568877, -1113}, {16580792590934885855, -1087},
{12353653155963782858, -1060}, {18408377700990114895, -1034},
{13715310171984221708, -1007}, {10218702384817765436, -980},
{15227053142812498563, -954}, {11345038669416679861, -927},
{16905424996341287883, -901}, {12595523146049147757, -874},
{9384396036005875287, -847}, {13983839803942852151, -821},
{10418772551374772303, -794}, {15525180923007089351, -768},
{11567161174868858868, -741}, {17236413322193710309, -715},
{12842128665889583758, -688}, {9568131466127621947, -661},
{14257626930069360058, -635}, {10622759856335341974, -608},
{15829145694278690180, -582}, {11793632577567316726, -555},
{17573882009934360870, -529}, {13093562431584567480, -502},
{9755464219737475723, -475}, {14536774485912137811, -449},
{10830740992659433045, -422}, {16139061738043178685, -396},
{12024538023802026127, -369}, {17917957937422433684, -343},
{13349918974505688015, -316}, {9946464728195732843, -289},
{14821387422376473014, -263}, {11042794154864902060, -236},
{16455045573212060422, -210}, {12259964326927110867, -183},
{18268770466636286478, -157}, {13611294676837538539, -130},
{10141204801825835212, -103}, {15111572745182864684, -77},
{11258999068426240000, -50}, {16777216000000000000, -24},
{12500000000000000000, 3}, {9313225746154785156, 30},
{13877787807814456755, 56}, {10339757656912845936, 83},
{15407439555097886824, 109}, {11479437019748901445, 136},
{17105694144590052135, 162}, {12744735289059618216, 189},
{9495567745759798747, 216}, {14149498560666738074, 242},
{10542197943230523224, 269}, {15709099088952724970, 295},
{11704190886730495818, 322}, {17440603504673385349, 348},
{12994262207056124023, 375}, {9681479787123295682, 402},
{14426529090290212157, 428}, {10748601772107342003, 455},
{16016664761464807395, 481}, {11933345169920330789, 508},
{17782069995880619868, 534}, {13248674568444952270, 561},
{9871031767461413346, 588}, {14708983551653345445, 614},
{10959046745042015199, 641}, {16330252207878254650, 667},
{12166986024289022870, 694}, {18130221999122236476, 720},
{13508068024458167312, 747}, {10064294952495520794, 774},
{14996968138956309548, 800}, {11173611982879273257, 827},
{16649979327439178909, 853}, {12405201291620119593, 880},
{9242595204427927429, 907}, {13772540099066387757, 933},
{10261342003245940623, 960}, {15290591125556738113, 986},
{11392378155556871081, 1013}, {16975966327722178521, 1039},
{12648080533535911531, 1066},
}
)
func find_cachedpow10(exp int64, k *int64) Fp {
one_log_ten := 0.30102999566398114
approx := int64(float64(-(exp + npowers)) * one_log_ten)
idx := int((approx - firstpower) / steppowers)
for {
current := int(exp + powers_ten[idx].exp + 64)
if current < expmin {
idx++
continue
}
if current > expmax {
idx--
continue
}
*k = (firstpower + int64(idx)*steppowers)
return powers_ten[idx]
}
}