diff --git a/cmd/main.go b/cmd/main.go index 7fab2d3..e40e385 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -64,8 +64,18 @@ func Serve(ctx *cli.Context) error { mainDomainSuffix = append([]byte{'.'}, mainDomainSuffix...) } + keyCache := cache.NewKeyValueCache() + challengeCache := cache.NewKeyValueCache() + // canonicalDomainCache stores canonical domains + var canonicalDomainCache = cache.NewKeyValueCache() + // dnsLookupCache stores DNS lookups for custom domains + var dnsLookupCache = cache.NewKeyValueCache() + // Create handler based on settings - handler := server.Handler(mainDomainSuffix, []byte(rawDomain), giteaRoot, rawInfoPage, giteaAPIToken, BlacklistedPaths, allowedCorsDomains) + handler := server.Handler(mainDomainSuffix, []byte(rawDomain), + giteaRoot, rawInfoPage, giteaAPIToken, + BlacklistedPaths, allowedCorsDomains, + dnsLookupCache, canonicalDomainCache) // Enable compression by wrapping the handler with the compression function provided by FastHTTP compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) @@ -93,11 +103,13 @@ func Serve(ctx *cli.Context) error { if err != nil { return fmt.Errorf("could not create database: %v", err) } - defer keyDatabase.Sync() // database has no close ... sync behave like it + defer keyDatabase.Sync() //nolint:errcheck // database has no close ... sync behave like it - keyCache := cache.NewKeyValueCache() - challengeCache := cache.NewKeyValueCache() - listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits, keyCache, challengeCache, keyDatabase)) + listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, + giteaRoot, giteaAPIToken, dnsProvider, + acmeUseRateLimits, + keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, + keyDatabase)) server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, challengeCache, keyDatabase) if enableHTTPServer { diff --git a/server/certificates.go b/server/certificates.go index 12d42a1..1493d03 100644 --- a/server/certificates.go +++ b/server/certificates.go @@ -38,7 +38,11 @@ import ( ) // TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. -func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider string, acmeUseRateLimits bool, keyCache, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) *tls.Config { +func TLSConfig(mainDomainSuffix []byte, + giteaRoot, giteaApiToken, dnsProvider string, + acmeUseRateLimits bool, + keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, + keyDatabase database.KeyDB) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -71,14 +75,14 @@ func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider st sni = string(sniBytes) } else { var targetRepo, targetBranch string - targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni, string(mainDomainSuffix)) + targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni, string(mainDomainSuffix), dnsLookupCache) if targetOwner == "" { // DNS not set up, return main certificate to redirect to the docs sniBytes = mainDomainSuffix sni = string(sniBytes) } else { _, _ = targetRepo, targetBranch - _, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaApiToken) + _, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !valid { sniBytes = mainDomainSuffix sni = string(sniBytes) diff --git a/server/domains.go b/server/domains.go index 5aeae96..cbbdef9 100644 --- a/server/domains.go +++ b/server/domains.go @@ -5,21 +5,18 @@ import ( "strings" "time" - "github.com/OrlovEvgeny/go-mcache" "github.com/valyala/fasthttp" + "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/upstream" ) // DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. var DnsLookupCacheTimeout = 15 * time.Minute -// dnsLookupCache stores DNS lookups for custom domains -var dnsLookupCache = mcache.New() - // 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 string) (targetOwner, targetRepo, targetBranch string) { +func getTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetGetKey) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string var err error @@ -68,11 +65,8 @@ func getTargetFromDNS(domain, mainDomainSuffix string) (targetOwner, targetRepo, // CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. var CanonicalDomainCacheTimeout = 15 * time.Minute -// canonicalDomainCache stores canonical domains -var canonicalDomainCache = mcache.New() - // checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). -func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string) (canonicalDomain string, valid bool) { +func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string, canonicalDomainCache cache.SetGetKey) (canonicalDomain string, valid bool) { domains := []string{} if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { domains = cachedValue.([]string) diff --git a/server/handler.go b/server/handler.go index a6a19f4..ba6cfdd 100644 --- a/server/handler.go +++ b/server/handler.go @@ -4,15 +4,20 @@ import ( "bytes" "strings" - "codeberg.org/codeberg/pages/html" - "codeberg.org/codeberg/pages/server/upstream" - "codeberg.org/codeberg/pages/server/utils" "github.com/rs/zerolog/log" "github.com/valyala/fasthttp" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/upstream" + "codeberg.org/codeberg/pages/server/utils" ) // Handler handles a single HTTP request to the web server. -func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaApiToken string, blacklistedPaths, allowedCorsDomains [][]byte) func(ctx *fasthttp.RequestCtx) { +func Handler(mainDomainSuffix, rawDomain []byte, + giteaRoot, rawInfoPage, giteaApiToken string, + blacklistedPaths, allowedCorsDomains [][]byte, + dnsLookupCache, canonicalDomainCache cache.SetGetKey) func(ctx *fasthttp.RequestCtx) { return func(ctx *fasthttp.RequestCtx) { log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger() @@ -108,7 +113,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp var tryUpstream = func() { // check if a canonical domain exists on a request on MainDomain if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { - canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaApiToken) + canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { canonicalPath := string(ctx.RequestURI()) if targetRepo != "pages" { @@ -242,7 +247,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp trimmedHostStr := string(trimmedHost) // Serve pages from external domains - targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr, string(mainDomainSuffix)) + targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr, string(mainDomainSuffix), dnsLookupCache) if targetOwner == "" { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return @@ -259,13 +264,13 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp // Try to use the given repo on the given branch or the default branch log.Debug().Msg("custom domain preparations, now trying with details from DNS") if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken) + canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !valid { html.ReturnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHostStr { // only redirect if the target is also a codeberg page! - targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) + targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix), dnsLookupCache) if targetOwner != "" { ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) return diff --git a/server/handler_test.go b/server/handler_test.go index c772dfc..3488ae7 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -5,6 +5,8 @@ import ( "github.com/valyala/fasthttp" "testing" "time" + + "codeberg.org/codeberg/pages/server/cache" ) func TestHandlerPerformance(t *testing.T) { @@ -16,6 +18,8 @@ func TestHandlerPerformance(t *testing.T) { "", [][]byte{[]byte("/.well-known/acme-challenge/")}, [][]byte{[]byte("raw.codeberg.org"), []byte("fonts.codeberg.org"), []byte("design.codeberg.org")}, + cache.NewKeyValueCache(), + cache.NewKeyValueCache(), ) ctx := &fasthttp.RequestCtx{