feat: URL normalisation, strict URLs and the one-hop redirect decision
- Normalize: https, www or bare host, double slashes and index files, trailing slash add/remove, optional ASCII lowercasing (defaults change nothing but scheme, slashes and index files) - strict URLs through signpost MatchPath: a path that names a record without being its address 301s to the record's address - resolve runs normalisation, redirects, strict URLs, then SEO; the location is the final address, so a visitor makes one hop - P1 warns when a record's canonical URL redirects
This commit is contained in:
68
classes/strict.go
Normal file
68
classes/strict.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package classes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/pact"
|
||||
)
|
||||
|
||||
// StrictMatch is the record behind a path and whether the path is its
|
||||
// address (D-20c). Key is "" when no published record answers the path.
|
||||
type StrictMatch struct {
|
||||
Key string
|
||||
Match *pact.ContentMatchResult
|
||||
Record *pact.ContentRecord
|
||||
// Canonical is the record's own path in the matched locale, in the
|
||||
// site's address form; "" when the descriptor answers none.
|
||||
Canonical string
|
||||
// Mismatch is true when the path names the record but is not its
|
||||
// address: a wrong category or parent, a locale prefix with another
|
||||
// locale's slug, a moved record. It then 301s to Canonical.
|
||||
Mismatch bool
|
||||
}
|
||||
|
||||
// Strict finds the published record behind path through the signpost
|
||||
// patterns (MatchPath) and checks every segment against the record's own
|
||||
// address (D-20c): a path that names a record without being its address
|
||||
// is a mismatch, so a wrong URL never answers ok. The comparison uses the
|
||||
// site's address form (opts), so a strict 301 and normalisation never
|
||||
// send a visitor back and forth.
|
||||
func (r *Resolver) Strict(ctx context.Context, path string, opts NormalizeOptions) (StrictMatch, error) {
|
||||
var out StrictMatch
|
||||
reg := r.Service.Registry()
|
||||
if reg == nil {
|
||||
return out, nil
|
||||
}
|
||||
unprefixed := r.Service.UnprefixedLocale()
|
||||
// Content patterns carry no trailing slash; the site's trailing-slash
|
||||
// rule is applied to the record's address below.
|
||||
key, match, err := reg.MatchPath(ctx, trimSlash(path), unprefixed)
|
||||
if err != nil || match == nil {
|
||||
return out, err
|
||||
}
|
||||
rec, err := reg.Describe(ctx, key, match.ID, unprefixed)
|
||||
if err != nil || rec == nil || !rec.Published {
|
||||
return out, err
|
||||
}
|
||||
out.Key, out.Match, out.Record = key, match, rec
|
||||
described := ""
|
||||
for _, l := range rec.Locales {
|
||||
if l.Locale == match.Locale {
|
||||
described = strings.TrimSpace(l.URL)
|
||||
}
|
||||
}
|
||||
if described == "" {
|
||||
out.Mismatch = !match.Exact
|
||||
return out, nil
|
||||
}
|
||||
du, err := url.Parse(described)
|
||||
if err != nil {
|
||||
return out, nil
|
||||
}
|
||||
canonical, _ := Normalize(&url.URL{Path: du.Path}, opts)
|
||||
out.Canonical = canonical.Path
|
||||
out.Mismatch = !match.Exact || canonical.Path != path
|
||||
return out, nil
|
||||
}
|
||||
Reference in New Issue
Block a user