Support multiple canonical-domain-files

This commit is contained in:
Max Stanley 2024-08-25 22:13:08 +01:00
parent 2410137438
commit b2d40c5154
13 changed files with 238 additions and 128 deletions

View file

@ -15,56 +15,91 @@ import (
var canonicalDomainCacheTimeout = 15 * time.Minute
// CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file).
func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix, canonicalDomainConfig string, canonicalDomainCache cache.ICache) (domain string, valid bool) {
canonicalDomainCacheKey := o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch + "/" + canonicalDomainConfig
func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainConfigs []string, canonicalDomainCache cache.ICache) (domain string, valid bool) {
canonicalDomainCacheKey := o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch + "/(" + strings.Join(canonicalDomainConfigs, "|") + ")"
var domains []string
// Check if this request is cached.
if cachedValue, ok := canonicalDomainCache.Get(canonicalDomainCacheKey); ok {
domains := cachedValue.([]string)
for _, domain := range domains {
if domain == actualDomain {
valid = true
break
}
}
return domains[0], valid
domains = cachedValue.([]string)
} else {
// Create cache entry for future invocations.
domains = o.canonicalDomainList(giteaClient, mainDomainSuffix, canonicalDomainConfigs)
// Add result to cache.
_ = canonicalDomainCache.Set(canonicalDomainCacheKey, domains, canonicalDomainCacheTimeout)
}
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)
}
var domains []string
for _, domain := range strings.Split(string(body), "\n") {
domain = strings.ToLower(domain)
domain = strings.TrimSpace(domain)
domain = strings.TrimPrefix(domain, "http://")
domain = strings.TrimPrefix(domain, "https://")
if domain != "" && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') {
domains = append(domains, domain)
}
for _, domain := range domains {
if domain == actualDomain {
valid = true
break
}
}
// Add [owner].[pages-domain] as valid domain.
domains = append(domains, o.TargetOwner+mainDomainSuffix)
if domains[len(domains)-1] == actualDomain {
valid = true
}
// If the target repository isn't called pages, add `/[repository]` to the
// previous valid domain.
if o.TargetRepo != "" && o.TargetRepo != "pages" {
domains[len(domains)-1] += "/" + o.TargetRepo
}
// Add result to cache.
_ = canonicalDomainCache.Set(canonicalDomainCacheKey, domains, canonicalDomainCacheTimeout)
// Return the first domain from the list and return if any of the domains
// matched the requested domain.
return domains[0], valid
}
// canonicalDomainList returns a list of normalized canonical domains as reported by the repository being served.
func (o *Options) canonicalDomainList(giteaClient *gitea.Client, mainDomainSuffix string, canonicalDomainConfigs []string) []string {
domainConfigMerge := ""
for _, canonicalDomainConfig := range canonicalDomainConfigs {
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)
continue
}
// Ensures files that don't end with a `\n` don't cause domains to be concatenated when combining files.
domainConfigMerge = domainConfigMerge + "\n" + string(body)
}
domains := normalizeDomainEntries(domainConfigMerge)
// Add [owner].[pages-domain] as valid domain.
domains = append(domains, o.pageMainDomain(mainDomainSuffix))
return domains
}
// pageMainDomain returns the [owner].[pages-domain] domain.
func (o *Options) pageMainDomain(mainDomainSuffix string) string {
pageMainDomain := o.TargetOwner + mainDomainSuffix
// If the target repository isn't called pages, add `/[repository]` to the
// previous valid domain.
if o.TargetRepo != "" && o.TargetRepo != "pages" {
pageMainDomain += "/" + o.TargetRepo
}
return pageMainDomain
}
// normalizeDomainEntries returns a list of domains, ill formatted domains are skipped.
// domainEntries is a new-line separated list of domains.
func normalizeDomainEntries(domainEntries string) []string {
domains := []string{}
for _, domain := range strings.Split(domainEntries, "\n") {
domain = strings.ToLower(domain)
domain = strings.TrimSpace(domain)
domain = strings.TrimPrefix(domain, "http://")
domain = strings.TrimPrefix(domain, "https://")
// Skip blank lines.
// Skip commented lines.
// Skip poorly formatted lines.
// Skip domains without '.'.
if domain == "" || strings.HasPrefix(domain, "#") || strings.ContainsAny(domain, "\t /") || !strings.ContainsRune(domain, '.') {
continue
}
domains = append(domains, domain)
}
return domains
}

View file

@ -0,0 +1,75 @@
package upstream
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestPageMainDomainGeneratesTheExpectedDomain(t *testing.T) {
defaultOptions := Options{
TargetOwner: "",
TargetRepo: "",
TargetBranch: "",
TargetPath: "",
Host: "",
TryIndexPages: false,
BranchTimestamp: time.Time{},
appendTrailingSlash: false,
redirectIfExists: "",
ServeRaw: false,
}
for _, tc := range []struct {
targetOwner string
targetRepo string
domainSuffix string
expectedDomain string
}{
{"foo", "", ".localhost.mock.directory", "foo.localhost.mock.directory"},
{"foo", "pages", ".localhost.mock.directory", "foo.localhost.mock.directory"},
{"foo", "bar", ".localhost.mock.directory", "foo.localhost.mock.directory/bar"},
} {
options := defaultOptions
options.TargetOwner = tc.targetOwner
options.TargetRepo = tc.targetRepo
actualDomain := options.pageMainDomain(tc.domainSuffix)
assert.Equal(t, tc.expectedDomain, actualDomain)
}
}
func TestNormalizeDomainEntries(t *testing.T) {
for _, tc := range []struct {
domain string
}{
{"abc.com"},
{"ABC.com"},
{" ABC.com"},
{"ABC.com "},
{" ABC.com "},
{"http://ABC.com"},
{"https://ABC.com"},
} {
actualDomains := normalizeDomainEntries(tc.domain)
expectedDomains := []string{"abc.com"}
assert.Equal(t, expectedDomains, actualDomains)
}
for _, tc := range []struct {
domains string
expectedDomains []string
}{
{"", []string{}},
{"ABC.com", []string{"abc.com"}},
{"ABC.com\nhttps://example.com", []string{"abc.com", "example.com"}},
{"\n\nABC.com\n\nhttps://example.com\n", []string{"abc.com", "example.com"}},
} {
actualDomains := normalizeDomainEntries(tc.domains)
assert.Equal(t, tc.expectedDomains, actualDomains)
}
}