Implement force redirects.

This commit is contained in:
Catherine
2025-10-02 16:41:23 +00:00
parent 157ceb8342
commit 1e01a12958
2 changed files with 36 additions and 19 deletions

View File

@@ -141,22 +141,28 @@ func getPage(w http.ResponseWriter, r *http.Request) error {
return err
}
entry = manifest.Contents[entryPath]
if entry == nil || entry.GetType() == Type_Invalid {
if !appliedRedirect {
originalURL := (&url.URL{Host: r.Host}).ResolveReference(r.URL)
redirectURL, redirectStatus := ApplyRedirects(manifest, originalURL)
if Is3xxHTTPStatus(redirectStatus) {
w.Header().Set("Location", redirectURL.String())
w.WriteHeader(int(redirectStatus))
fmt.Fprintf(w, "see %s\n", redirectURL.String())
return nil
} else if redirectURL != nil {
entryPath = strings.TrimPrefix(redirectURL.Path, "/")
status = int(redirectStatus)
appliedRedirect = true
continue
}
if !appliedRedirect {
redirectKind := RedirectAny
if entry != nil && entry.GetType() != Type_Invalid {
redirectKind = RedirectForce
}
originalURL := (&url.URL{Host: r.Host}).ResolveReference(r.URL)
redirectURL, redirectStatus := ApplyRedirects(manifest, originalURL, redirectKind)
if Is3xxHTTPStatus(redirectStatus) {
w.Header().Set("Location", redirectURL.String())
w.WriteHeader(int(redirectStatus))
fmt.Fprintf(w, "see %s\n", redirectURL.String())
return nil
} else if redirectURL != nil {
entryPath = strings.TrimPrefix(redirectURL.Path, "/")
status = int(redirectStatus)
// Apply user redirects at most once; if something ends in a loop, it should be
// the user agent, not the pages server.
appliedRedirect = true
continue
}
}
if entry == nil || entry.GetType() == Type_Invalid {
status = 404
if entryPath != notFoundPage {
entryPath = notFoundPage

View File

@@ -80,9 +80,6 @@ func validateRule(rule redirects.Rule) error {
if toURL.Host != "" && !Is3xxHTTPStatus(uint(rule.Status)) {
return fmt.Errorf("'to' URL may only include a hostname for 3xx status rules")
}
if rule.Force {
return fmt.Errorf("force redirects are not supported")
}
return nil
}
@@ -130,10 +127,24 @@ func toOrFromComponent(to, from string) string {
}
}
func ApplyRedirects(manifest *Manifest, fromURL *url.URL) (toURL *url.URL, status uint) {
type RedirectKind int
const (
RedirectAny RedirectKind = iota
RedirectForce
)
func ApplyRedirects(
manifest *Manifest, fromURL *url.URL, kind RedirectKind,
) (
toURL *url.URL, status uint,
) {
fromSegments := pathSegments(fromURL.Path)
next:
for _, rule := range manifest.Redirects {
if kind == RedirectForce && !*rule.Force {
continue
}
// check if the rule matches fromURL
ruleFromURL, _ := url.Parse(*rule.From) // pre-validated in `validateRule`
if ruleFromURL.Scheme != "" && fromURL.Scheme != ruleFromURL.Scheme {