mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-08-18 16:37:18 +02:00
125 lines
4.0 KiB
Go
125 lines
4.0 KiB
Go
// Copyright 2023 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package http
|
|
|
|
import "math"
|
|
|
|
// A routingIndex optimizes conflict detection by indexing patterns.
|
|
//
|
|
// The basic idea is to rule out patterns that cannot conflict with a given
|
|
// pattern because they have a different literal in a corresponding segment.
|
|
// See the comments in [routingIndex.possiblyConflictingPatterns] for more details.
|
|
type routingIndex struct {
|
|
// map from a particular segment position and value to all registered patterns
|
|
// with that value in that position.
|
|
// For example, the key {1, "b"} would hold the patterns "/a/b" and "/a/b/c"
|
|
// but not "/a", "b/a", "/a/c" or "/a/{x}".
|
|
segments map[routingIndexKey][]*pattern
|
|
// All patterns that end in a multi wildcard (including trailing slash).
|
|
// We do not try to be clever about indexing multi patterns, because there
|
|
// are unlikely to be many of them.
|
|
multis []*pattern
|
|
}
|
|
|
|
type routingIndexKey struct {
|
|
pos int // 0-based segment position
|
|
s string // literal, or empty for wildcard
|
|
}
|
|
|
|
func (idx *routingIndex) addPattern(pat *pattern) {
|
|
if pat.lastSegment().multi {
|
|
idx.multis = append(idx.multis, pat)
|
|
} else {
|
|
if idx.segments == nil {
|
|
idx.segments = map[routingIndexKey][]*pattern{}
|
|
}
|
|
for pos, seg := range pat.segments {
|
|
key := routingIndexKey{pos: pos, s: ""}
|
|
if !seg.wild {
|
|
key.s = seg.s
|
|
}
|
|
idx.segments[key] = append(idx.segments[key], pat)
|
|
}
|
|
}
|
|
}
|
|
|
|
// possiblyConflictingPatterns calls f on all patterns that might conflict with
|
|
// pat. If f returns a non-nil error, possiblyConflictingPatterns returns immediately
|
|
// with that error.
|
|
//
|
|
// To be correct, possiblyConflictingPatterns must include all patterns that
|
|
// might conflict. But it may also include patterns that cannot conflict.
|
|
// For instance, an implementation that returns all registered patterns is correct.
|
|
// We use this fact throughout, simplifying the implementation by returning more
|
|
// patterns that we might need to.
|
|
func (idx *routingIndex) possiblyConflictingPatterns(pat *pattern, f func(*pattern) error) (err error) {
|
|
// Terminology:
|
|
// dollar pattern: one ending in "{$}"
|
|
// multi pattern: one ending in a trailing slash or "{x...}" wildcard
|
|
// ordinary pattern: neither of the above
|
|
|
|
// apply f to all the pats, stopping on error.
|
|
apply := func(pats []*pattern) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, p := range pats {
|
|
err = f(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Our simple indexing scheme doesn't try to prune multi patterns; assume
|
|
// any of them can match the argument.
|
|
if err := apply(idx.multis); err != nil {
|
|
return err
|
|
}
|
|
if pat.lastSegment().s == "/" {
|
|
// All paths that a dollar pattern matches end in a slash; no paths that
|
|
// an ordinary pattern matches do. So only other dollar or multi
|
|
// patterns can conflict with a dollar pattern. Furthermore, conflicting
|
|
// dollar patterns must have the {$} in the same position.
|
|
return apply(idx.segments[routingIndexKey{s: "/", pos: len(pat.segments) - 1}])
|
|
}
|
|
// For ordinary and multi patterns, the only conflicts can be with a multi,
|
|
// or a pattern that has the same literal or a wildcard at some literal
|
|
// position.
|
|
// We could intersect all the possible matches at each position, but we
|
|
// do something simpler: we find the position with the fewest patterns.
|
|
var lmin, wmin []*pattern
|
|
min := math.MaxInt
|
|
hasLit := false
|
|
for i, seg := range pat.segments {
|
|
if seg.multi {
|
|
break
|
|
}
|
|
if !seg.wild {
|
|
hasLit = true
|
|
lpats := idx.segments[routingIndexKey{s: seg.s, pos: i}]
|
|
wpats := idx.segments[routingIndexKey{s: "", pos: i}]
|
|
if sum := len(lpats) + len(wpats); sum < min {
|
|
lmin = lpats
|
|
wmin = wpats
|
|
min = sum
|
|
}
|
|
}
|
|
}
|
|
if hasLit {
|
|
apply(lmin)
|
|
apply(wmin)
|
|
return err
|
|
}
|
|
|
|
// This pattern is all wildcards.
|
|
// Check it against everything.
|
|
for _, pats := range idx.segments {
|
|
apply(pats)
|
|
}
|
|
return err
|
|
}
|