From 5b6eecc75fd1c700dd5d9e5c4d6ab90ccdae1f1b Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sun, 24 Mar 2024 20:24:32 +0100 Subject: [PATCH] Add redis for caching, first try during a train ride so expect it to not be working yet --- go.mod | 5 ++- go.sum | 10 +++++ server/cache/interface.go | 4 +- server/cache/memory.go | 28 ++++++++++++- server/cache/redis.go | 47 +++++++++++++++++++++ server/certificates/cached_challengers.go | 4 +- server/certificates/certificates.go | 5 ++- server/dns/dns.go | 5 +-- server/gitea/client.go | 50 ++++++++++++++++------- server/handler/handler_custom_domain.go | 3 +- server/upstream/domains.go | 5 ++- server/upstream/redirects.go | 15 ++++++- 12 files changed, 149 insertions(+), 32 deletions(-) create mode 100644 server/cache/redis.go diff --git a/go.mod b/go.mod index 47e1e71..985c4b1 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ toolchain go1.21.4 require ( code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a + github.com/creasty/defaults v1.7.0 github.com/go-acme/lego/v4 v4.5.3 github.com/go-sql-driver/mysql v1.6.0 github.com/joho/godotenv v1.4.0 @@ -14,6 +15,7 @@ require ( github.com/mattn/go-sqlite3 v1.14.16 github.com/microcosm-cc/bluemonday v1.0.26 github.com/pelletier/go-toml/v2 v2.1.0 + github.com/redis/go-redis/v9 v9.5.1 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 @@ -42,13 +44,14 @@ require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cloudflare/cloudflare-go v0.20.0 // indirect github.com/cpu/goacmedns v0.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect - github.com/creasty/defaults v1.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/deepmap/oapi-codegen v1.6.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dnsimple/dnsimple-go v0.70.1 // indirect github.com/exoscale/egoscale v0.67.0 // indirect diff --git a/go.sum b/go.sum index 1a10599..8befa87 100644 --- a/go.sum +++ b/go.sum @@ -97,6 +97,10 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -105,6 +109,8 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -143,6 +149,8 @@ github.com/deepmap/oapi-codegen v1.6.1 h1:2BvsmRb6pogGNtr8Ann+esAbSKFXx2CZN18VpA github.com/deepmap/oapi-codegen v1.6.1/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= @@ -615,6 +623,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= +github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjIi3L6QmL/V9vpJPbc/j/F2u55d3fs= diff --git a/server/cache/interface.go b/server/cache/interface.go index b3412cc..acc4b5b 100644 --- a/server/cache/interface.go +++ b/server/cache/interface.go @@ -4,7 +4,7 @@ import "time" // ICache is an interface that defines how the pages server interacts with the cache. type ICache interface { - Set(key string, value interface{}, ttl time.Duration) error - Get(key string) (interface{}, bool) + Set(key string, value string, ttl time.Duration) error + Get(key string) (string, bool) Remove(key string) } diff --git a/server/cache/memory.go b/server/cache/memory.go index 093696f..7dfc18c 100644 --- a/server/cache/memory.go +++ b/server/cache/memory.go @@ -1,7 +1,31 @@ package cache -import "github.com/OrlovEvgeny/go-mcache" +import ( + "github.com/OrlovEvgeny/go-mcache" + "time" +) + +type MCache struct { + mcache *mcache.CacheDriver +} + +func (m *MCache) Set(key string, value string, ttl time.Duration) error { + return m.mcache.Set(key, value, ttl) +} + +func (m *MCache) Get(key string) (string, bool) { + val, ok := m.mcache.Get(key) + if ok { + return val.(string), true + } else { + return "", false + } +} + +func (m *MCache) Remove(key string) { + m.mcache.Remove(key) +} func NewInMemoryCache() ICache { - return mcache.New() + return &MCache{mcache.New()} } diff --git a/server/cache/redis.go b/server/cache/redis.go new file mode 100644 index 0000000..d2ba8df --- /dev/null +++ b/server/cache/redis.go @@ -0,0 +1,47 @@ +package cache + +import ( + "context" + "github.com/redis/go-redis/v9" + "github.com/rs/zerolog/log" + "time" +) + +type RedisCache struct { + ctx context.Context + rdb *redis.Client +} + +func (r *RedisCache) Set(key string, value string, ttl time.Duration) error { + return r.rdb.Set(r.ctx, key, value, ttl).Err() +} + +func (r *RedisCache) Get(key string) (string, bool) { + val, err := r.rdb.Get(r.ctx, key).Result() + if err != nil { + if err == redis.Nil { + log.Error().Err(err).Str("key", key).Msg("Couldn't request key from cache.") + } + return "", false + } else { + return val, true + } +} + +func (r *RedisCache) Remove(key string) { + err := r.rdb.Del(r.ctx, key).Err() + if err == nil { + log.Error().Err(err).Str("key", key).Msg("Couldn't delete key from cache.") + } +} + +func NewRedisCache() ICache { + return &RedisCache{ + context.Background(), + redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }), + } +} diff --git a/server/certificates/cached_challengers.go b/server/certificates/cached_challengers.go index 39439fb..d11d3f4 100644 --- a/server/certificates/cached_challengers.go +++ b/server/certificates/cached_challengers.go @@ -60,12 +60,12 @@ func SetupHTTPACMEChallengeServer(challengeCache cache.ICache, sslPort uint) htt // it's an acme request if strings.HasPrefix(ctx.Path(), challengePath) { challenge, ok := challengeCache.Get(domain + "/" + strings.TrimPrefix(ctx.Path(), challengePath)) - if !ok || challenge == nil { + if !ok || challenge == "" { log.Info().Msgf("HTTP-ACME challenge for '%s' failed: token not found", domain) ctx.String("no challenge for this token", http.StatusNotFound) } log.Info().Msgf("HTTP-ACME challenge for '%s' succeeded", domain) - ctx.String(challenge.(string)) + ctx.String(challenge) return } diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 67219dd..db1ea96 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "errors" "fmt" + "github.com/OrlovEvgeny/go-mcache" "strconv" "strings" "time" @@ -31,7 +32,7 @@ func TLSConfig(mainDomainSuffix string, giteaClient *gitea.Client, acmeClient *AcmeClient, firstDefaultBranch string, - keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.ICache, + keyCache *mcache.CacheDriver, challengeCache cache.ICache, dnsLookupCache *mcache.CacheDriver, canonicalDomainCache cache.ICache, certDB database.CertDB, noDNS01 bool, rawDomain string, @@ -56,7 +57,7 @@ func TLSConfig(mainDomainSuffix string, if !ok { return nil, errors.New("no challenge for this domain") } - cert, err := tlsalpn01.ChallengeCert(domain, challenge.(string)) + cert, err := tlsalpn01.ChallengeCert(domain, challenge) if err != nil { return nil, err } diff --git a/server/dns/dns.go b/server/dns/dns.go index 970f0c0..9904396 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -1,11 +1,10 @@ package dns import ( + "github.com/OrlovEvgeny/go-mcache" "net" "strings" "time" - - "codeberg.org/codeberg/pages/server/cache" ) // lookupCacheTimeout specifies the timeout for the DNS lookup cache. @@ -15,7 +14,7 @@ var defaultPagesRepo = "pages" // GetTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // If everything is fine, it returns the target data. -func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLookupCache cache.ICache) (targetOwner, targetRepo, targetBranch string) { +func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLookupCache *mcache.CacheDriver) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string var err error diff --git a/server/gitea/client.go b/server/gitea/client.go index 5955bfb..067bad9 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -115,8 +115,17 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str log := log.With().Str("cache_key", cacheKey).Logger() log.Trace().Msg("try file in cache") // handle if cache entry exist - if cache, ok := client.responseCache.Get(cacheKey); ok { - cache := cache.(FileResponse) + if cacheMetadata, ok := client.responseCache.Get(cacheKey + "|Metadata"); ok { + cacheMetadataParts := strings.Split(cacheMetadata, "\n") + cache := FileResponse{ + Exists: cacheMetadataParts[0] == "true", + IsSymlink: cacheMetadataParts[1] == "true", + ETag: cacheMetadataParts[2], + } + cacheBodyString, _ := client.responseCache.Get(cacheKey + "|Body") + cache.Body = []byte(cacheBodyString) + // TODO: don't grab the content from the cache if the ETag matches?! + cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey) // TODO: check against some timestamp mismatch?!? if cache.Exists { @@ -130,6 +139,7 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil } else if cache.IsEmpty() { log.Debug().Msg("[cache] is empty") + // TODO: empty files aren't cached anyways; but when closing the issue please make sure that a missing body cache key is also handled correctly. } } } @@ -164,7 +174,12 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str ETag: resp.Header.Get(ETagHeader), } log.Trace().Msgf("file response has %d bytes", len(fileResponse.Body)) - if err := client.responseCache.Set(cacheKey, fileResponse, fileCacheTimeout); err != nil { + metadataStr := strconv.FormatBool(fileResponse.Exists) + "\n" + strconv.FormatBool(fileResponse.IsSymlink) + "\n" + fileResponse.ETag + if err := client.responseCache.Set(cacheKey+"|Metadata", metadataStr, fileCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + // TODO: Test with binary files, as we convert []byte to string! Using []byte values might makes more sense anyways. + if err := client.responseCache.Set(cacheKey+"|Body", string(fileResponse.Body), fileCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } @@ -187,13 +202,11 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str ETag: resp.Header.Get(ETagHeader), MimeType: mimeType, } + // TODO: dafuq... return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil case http.StatusNotFound: - if err := client.responseCache.Set(cacheKey, FileResponse{ - Exists: false, - ETag: resp.Header.Get(ETagHeader), - }, fileCacheTimeout); err != nil { + if err := client.responseCache.Set(cacheKey+"|Metadata", "false\nfalse\n"+resp.Header.Get(ETagHeader), fileCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } @@ -208,21 +221,28 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchName string) (*BranchTimestamp, error) { cacheKey := fmt.Sprintf("%s/%s/%s/%s", branchTimestampCacheKeyPrefix, repoOwner, repoName, branchName) - if stamp, ok := client.responseCache.Get(cacheKey); ok && stamp != nil { - branchTimeStamp := stamp.(*BranchTimestamp) - if branchTimeStamp.notFound { + if stamp, ok := client.responseCache.Get(cacheKey); ok { + if stamp == "" { log.Trace().Msgf("[cache] use branch %q not found", branchName) return &BranchTimestamp{}, ErrorNotFound } log.Trace().Msgf("[cache] use branch %q exist", branchName) - return branchTimeStamp, nil + // This comes from the refactoring of the caching library. + // The branch as reported by the API was stored in the cache, and I'm not sure if there are + // situations where it differs from the name in the request, hence this is left here. + stampParts := strings.SplitN(stamp, "", 2) + stampTime, _ := time.Parse(time.RFC3339, stampParts[0]) + return &BranchTimestamp{ + Branch: stampParts[1], + Timestamp: stampTime, + }, nil } branch, resp, err := client.sdkClient.GetRepoBranch(repoOwner, repoName, branchName) if err != nil { if resp != nil && resp.StatusCode == http.StatusNotFound { log.Trace().Msgf("[cache] set cache branch %q not found", branchName) - if err := client.responseCache.Set(cacheKey, &BranchTimestamp{Branch: branchName, notFound: true}, branchExistenceCacheTimeout); err != nil { + if err := client.responseCache.Set(cacheKey, "", branchExistenceCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } return &BranchTimestamp{}, ErrorNotFound @@ -239,7 +259,7 @@ func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchNam } log.Trace().Msgf("set cache branch [%s] exist", branchName) - if err := client.responseCache.Set(cacheKey, stamp, branchExistenceCacheTimeout); err != nil { + if err := client.responseCache.Set(cacheKey, stamp.Timestamp.Format(time.RFC3339)+"|"+stamp.Branch, branchExistenceCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } return stamp, nil @@ -248,8 +268,8 @@ func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchNam func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (string, error) { cacheKey := fmt.Sprintf("%s/%s/%s", defaultBranchCacheKeyPrefix, repoOwner, repoName) - if branch, ok := client.responseCache.Get(cacheKey); ok && branch != nil { - return branch.(string), nil + if branch, ok := client.responseCache.Get(cacheKey); ok { + return branch, nil } repo, resp, err := client.sdkClient.GetRepo(repoOwner, repoName) diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 82953f9..ee7a629 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -1,6 +1,7 @@ package handler import ( + "github.com/OrlovEvgeny/go-mcache" "net/http" "path" "strings" @@ -19,7 +20,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g trimmedHost string, pathElements []string, firstDefaultBranch string, - dnsLookupCache, canonicalDomainCache, redirectsCache cache.ICache, + dnsLookupCache *mcache.CacheDriver, canonicalDomainCache, redirectsCache cache.ICache, ) { // Serve pages from custom domains targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) diff --git a/server/upstream/domains.go b/server/upstream/domains.go index d53d586..ac070bf 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -20,7 +20,7 @@ const canonicalDomainConfig = ".domains" func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.ICache) (domain string, valid bool) { // Check if this request is cached. if cachedValue, ok := canonicalDomainCache.Get(o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch); ok { - domains := cachedValue.([]string) + domains := strings.Split(cachedValue, "\n") for _, domain := range domains { if domain == actualDomain { valid = true @@ -33,6 +33,7 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig) if err != nil && !errors.Is(err, gitea.ErrorNotFound) { log.Error().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) + // TODO: WTF we just continue?! } var domains []string @@ -62,7 +63,7 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, } // Add result to cache. - _ = canonicalDomainCache.Set(o.TargetOwner+"/"+o.TargetRepo+"/"+o.TargetBranch, domains, canonicalDomainCacheTimeout) + _ = canonicalDomainCache.Set(o.TargetOwner+"/"+o.TargetRepo+"/"+o.TargetBranch, strings.Join(domains, "\n"), canonicalDomainCacheTimeout) // Return the first domain from the list and return if any of the domains // matched the requested domain. diff --git a/server/upstream/redirects.go b/server/upstream/redirects.go index dd36a84..3a19928 100644 --- a/server/upstream/redirects.go +++ b/server/upstream/redirects.go @@ -1,6 +1,7 @@ package upstream import ( + "encoding/json" "strconv" "strings" "time" @@ -29,7 +30,12 @@ func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.I // Check for cached redirects if cachedValue, ok := redirectsCache.Get(cacheKey); ok { - redirects = cachedValue.([]Redirect) + redirects := []Redirect{} + err := json.Unmarshal([]byte(cachedValue), redirects) + if err != nil { + log.Error().Err(err).Msgf("could not parse redirects for key %s", cacheKey) + // It's okay to continue, the array stays empty. + } } else { // Get _redirects file and parse body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, redirectsConfig) @@ -58,7 +64,12 @@ func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.I }) } } - _ = redirectsCache.Set(cacheKey, redirects, redirectsCacheTimeout) + redirectsJson, err := json.Marshal(redirects) + if err != nil { + log.Error().Err(err).Msgf("could not store redirects for key %s", cacheKey) + } else { + _ = redirectsCache.Set(cacheKey, string(redirectsJson), redirectsCacheTimeout) + } } return redirects }