mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2025-04-24 13:56:57 +00:00
Initial _redirects implementation
This commit is contained in:
parent
9d769aeee7
commit
4b873a4792
8 changed files with 106 additions and 15 deletions
|
@ -80,6 +80,8 @@ func Serve(ctx *cli.Context) error {
|
|||
canonicalDomainCache := cache.NewKeyValueCache()
|
||||
// dnsLookupCache stores DNS lookups for custom domains
|
||||
dnsLookupCache := cache.NewKeyValueCache()
|
||||
// redirectsCache stores redirects in _redirects files
|
||||
redirectsCache := cache.NewKeyValueCache()
|
||||
// clientResponseCache stores responses from the Gitea server
|
||||
clientResponseCache := cache.NewKeyValueCache()
|
||||
|
||||
|
@ -93,7 +95,7 @@ func Serve(ctx *cli.Context) error {
|
|||
giteaClient,
|
||||
rawInfoPage,
|
||||
BlacklistedPaths, allowedCorsDomains,
|
||||
dnsLookupCache, canonicalDomainCache)
|
||||
dnsLookupCache, canonicalDomainCache, redirectsCache)
|
||||
|
||||
httpHandler := server.SetupHTTPACMEChallengeServer(challengeCache)
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ func Handler(mainDomainSuffix, rawDomain string,
|
|||
giteaClient *gitea.Client,
|
||||
rawInfoPage string,
|
||||
blacklistedPaths, allowedCorsDomains []string,
|
||||
dnsLookupCache, canonicalDomainCache cache.SetGetKey,
|
||||
dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger()
|
||||
|
@ -93,21 +93,21 @@ func Handler(mainDomainSuffix, rawDomain string,
|
|||
mainDomainSuffix, rawInfoPage,
|
||||
trimmedHost,
|
||||
pathElements,
|
||||
canonicalDomainCache)
|
||||
canonicalDomainCache, redirectsCache)
|
||||
} else if strings.HasSuffix(trimmedHost, mainDomainSuffix) {
|
||||
log.Debug().Msg("subdomain request detecded")
|
||||
handleSubDomain(log, ctx, giteaClient,
|
||||
mainDomainSuffix,
|
||||
trimmedHost,
|
||||
pathElements,
|
||||
canonicalDomainCache)
|
||||
canonicalDomainCache, redirectsCache)
|
||||
} else {
|
||||
log.Debug().Msg("custom domain request detecded")
|
||||
handleCustomDomain(log, ctx, giteaClient,
|
||||
mainDomainSuffix,
|
||||
trimmedHost,
|
||||
pathElements,
|
||||
dnsLookupCache, canonicalDomainCache)
|
||||
dnsLookupCache, canonicalDomainCache, redirectsCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g
|
|||
mainDomainSuffix string,
|
||||
trimmedHost string,
|
||||
pathElements []string,
|
||||
dnsLookupCache, canonicalDomainCache cache.SetGetKey,
|
||||
dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey,
|
||||
) {
|
||||
// Serve pages from custom domains
|
||||
targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, dnsLookupCache)
|
||||
|
@ -63,7 +63,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g
|
|||
}
|
||||
|
||||
log.Debug().Msg("tryBranch, now trying upstream 7")
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache)
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie
|
|||
mainDomainSuffix, rawInfoPage string,
|
||||
trimmedHost string,
|
||||
pathElements []string,
|
||||
canonicalDomainCache cache.SetGetKey,
|
||||
canonicalDomainCache, redirectsCache cache.SetGetKey,
|
||||
) {
|
||||
// Serve raw content from RawDomain
|
||||
log.Debug().Msg("raw domain")
|
||||
|
@ -41,7 +41,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie
|
|||
TargetPath: path.Join(pathElements[3:]...),
|
||||
}, true); works {
|
||||
log.Trace().Msg("tryUpstream: serve raw domain with specified branch")
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache)
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||
return
|
||||
}
|
||||
log.Debug().Msg("missing branch info")
|
||||
|
@ -58,7 +58,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie
|
|||
TargetPath: path.Join(pathElements[2:]...),
|
||||
}, true); works {
|
||||
log.Trace().Msg("tryUpstream: serve raw domain with default branch")
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache)
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||
} else {
|
||||
html.ReturnErrorPage(ctx,
|
||||
fmt.Sprintf("raw domain could not find repo '%s/%s' or repo is empty", targetOpt.TargetOwner, targetOpt.TargetRepo),
|
||||
|
|
|
@ -19,7 +19,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
|
|||
mainDomainSuffix string,
|
||||
trimmedHost string,
|
||||
pathElements []string,
|
||||
canonicalDomainCache cache.SetGetKey,
|
||||
canonicalDomainCache, redirectsCache cache.SetGetKey,
|
||||
) {
|
||||
// Serve pages from subdomains of MainDomainSuffix
|
||||
log.Debug().Msg("main domain suffix")
|
||||
|
@ -51,7 +51,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
|
|||
TargetPath: path.Join(pathElements[2:]...),
|
||||
}, true); works {
|
||||
log.Trace().Msg("tryUpstream: serve with specified repo and branch")
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache)
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||
} else {
|
||||
html.ReturnErrorPage(ctx,
|
||||
fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo),
|
||||
|
@ -72,7 +72,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
|
|||
TargetPath: path.Join(pathElements[1:]...),
|
||||
}, true); works {
|
||||
log.Trace().Msg("tryUpstream: serve default pages repo with specified branch")
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache)
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||
} else {
|
||||
html.ReturnErrorPage(ctx,
|
||||
fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo),
|
||||
|
@ -94,7 +94,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
|
|||
TargetPath: path.Join(pathElements[1:]...),
|
||||
}, false); works {
|
||||
log.Debug().Msg("tryBranch, now trying upstream 5")
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache)
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
|
|||
TargetPath: path.Join(pathElements...),
|
||||
}, false); works {
|
||||
log.Debug().Msg("tryBranch, now trying upstream 6")
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache)
|
||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ func TestHandlerPerformance(t *testing.T) {
|
|||
[]string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"},
|
||||
cache.NewKeyValueCache(),
|
||||
cache.NewKeyValueCache(),
|
||||
cache.NewKeyValueCache(),
|
||||
)
|
||||
|
||||
testCase := func(uri string, status int) {
|
||||
|
|
|
@ -18,6 +18,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client,
|
|||
mainDomainSuffix, trimmedHost string,
|
||||
options *upstream.Options,
|
||||
canonicalDomainCache cache.SetGetKey,
|
||||
redirectsCache cache.SetGetKey,
|
||||
) {
|
||||
// check if a canonical domain exists on a request on MainDomain
|
||||
if strings.HasSuffix(trimmedHost, mainDomainSuffix) {
|
||||
|
@ -38,6 +39,41 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client,
|
|||
// Add host for debugging.
|
||||
options.Host = trimmedHost
|
||||
|
||||
// Check for redirects
|
||||
redirects := options.GetRedirects(giteaClient, redirectsCache)
|
||||
if len(redirects) > 0 {
|
||||
for _, redirect := range redirects {
|
||||
reqUrl := ctx.Req.RequestURI
|
||||
trimmedFromUrl := strings.TrimSuffix(redirect.From, "/*")
|
||||
if strings.TrimSuffix(redirect.From, "/") == strings.TrimSuffix(reqUrl, "/") {
|
||||
if redirect.StatusCode == 200 {
|
||||
options.TargetPath = redirect.To
|
||||
} else {
|
||||
ctx.Redirect(redirect.To, redirect.StatusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(redirect.From, "/*") && strings.HasPrefix(reqUrl, trimmedFromUrl) {
|
||||
if strings.Contains(redirect.To, ":splat") {
|
||||
splatUrl := strings.ReplaceAll(redirect.To, ":splat", strings.TrimPrefix(reqUrl, trimmedFromUrl))
|
||||
if redirect.StatusCode == 200 {
|
||||
options.TargetPath = splatUrl
|
||||
} else {
|
||||
ctx.Redirect(splatUrl, redirect.StatusCode)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if redirect.StatusCode == 200 {
|
||||
options.TargetPath = redirect.To
|
||||
} else {
|
||||
ctx.Redirect(redirect.To, redirect.StatusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to request the file from the Gitea API
|
||||
if !options.Upstream(ctx, giteaClient) {
|
||||
html.ReturnErrorPage(ctx, "", ctx.StatusCode)
|
||||
|
|
52
server/upstream/redirects.go
Normal file
52
server/upstream/redirects.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package upstream
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"codeberg.org/codeberg/pages/server/cache"
|
||||
"codeberg.org/codeberg/pages/server/gitea"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type Redirect struct {
|
||||
From string
|
||||
To string
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
// redirectsCacheTimeout specifies the timeout for the redirects cache.
|
||||
var redirectsCacheTimeout = 10 * time.Minute
|
||||
|
||||
const redirectsConfig = "_redirects"
|
||||
|
||||
// GetRedirects returns redirects specified in the _redirects file.
|
||||
func (o *Options) GetRedirects(giteaClient *gitea.Client, redirectsCache cache.SetGetKey) []Redirect {
|
||||
var redirects []Redirect
|
||||
|
||||
if cachedValue, ok := redirectsCache.Get(o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch); ok {
|
||||
redirects = cachedValue.([]Redirect)
|
||||
} else {
|
||||
body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, redirectsConfig)
|
||||
if err == nil {
|
||||
for _, line := range strings.Split(string(body), "\n") {
|
||||
redirectArr := strings.Fields(line)
|
||||
if len(redirectArr) == 3 {
|
||||
statusCode, err := strconv.Atoi(redirectArr[2])
|
||||
if err != nil {
|
||||
log.Info().Err(err).Msgf("could not read %s of %s/%s", redirectsConfig, o.TargetOwner, o.TargetRepo)
|
||||
}
|
||||
|
||||
redirects = append(redirects, Redirect{
|
||||
From: redirectArr[0],
|
||||
To: redirectArr[1],
|
||||
StatusCode: statusCode,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = redirectsCache.Set(o.TargetOwner+"/"+o.TargetRepo+"/"+o.TargetBranch, redirects, redirectsCacheTimeout)
|
||||
}
|
||||
return redirects
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue