/
rewriter.go
124 lines (104 loc) · 2.61 KB
/
rewriter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package heproxy
import (
"net/url"
"strings"
)
// Rewriter rewrites url to a new url
type Rewriter interface {
Rewrite(*url.URL) *url.URL
}
// LinearRewriter holds rulesets for remapping URLs
type LinearRewriter struct {
rulesets []*RuleSet
}
// NewLinearRewriter returns a Remapper which can remap URLs based on the RuleSets
func NewLinearRewriter(rs []*RuleSet) Rewriter {
return &LinearRewriter{
rulesets: rs,
}
}
// Rewrite returns a new URL based on the contained rulesets
func (r *LinearRewriter) Rewrite(u *url.URL) *url.URL {
// Find a possible ruleset that matches this URL.
for _, r := range r.rulesets {
if r.Includes(u) {
return r.Rewrite(u)
}
}
return u
}
type trieNode struct {
// If this is nil, it's an internal node.
Rset *RuleSet
children map[string]*trieNode
}
// TrieRewriter utilizes a trie tree for rewriting urls based on rulesets
type TrieRewriter struct {
root *trieNode
}
// NewTrieRewriter returns a TrieRewriter for remapping urls.
func NewTrieRewriter(rss []*RuleSet) Rewriter {
t := new(TrieRewriter)
t.build(rss)
return t
}
// Rewrite returns a new URL based on the contained rulesets
func (t *TrieRewriter) Rewrite(u *url.URL) *url.URL {
host := strings.Split(u.Host, ":")[0]
bits := strings.Split(host, ".")
rs := t.root.findMatch(bits)
if rs != nil && rs.Includes(u) {
return rs.Rewrite(u)
}
return u
}
func (t *TrieRewriter) build(rss []*RuleSet) {
if t.root == nil {
t.root = &trieNode{children: make(map[string]*trieNode)}
}
for _, rs := range rss {
for _, target := range rs.Targets {
bits := strings.Split(target.Host, ".")
t.root.add(bits, rs)
}
}
}
func (t *trieNode) add(pieces []string, rs *RuleSet) {
if len(pieces) == 0 {
t.Rset = rs
return
}
if next, ok := t.children[pieces[0]]; !ok {
// add to current children, and recurse
next = &trieNode{children: make(map[string]*trieNode)}
t.children[pieces[0]] = next
next.add(pieces[1:], rs)
} else {
next.add(pieces[1:], rs)
}
}
func (t *trieNode) findMatch(target []string) *RuleSet {
// step through target. If t.children has a '*', but no direct match, explore each child searching for a match.
switch len(target) {
case 0:
return t.Rset
case 1:
if node, ok := t.children[target[0]]; !ok {
// is there a wild? If so, return the Rset
if wild, ok := t.children["*"]; ok {
return wild.findMatch(target[1:])
}
} else {
return node.findMatch(target[1:])
}
default:
if node, ok := t.children[target[0]]; !ok {
if wild, ok := t.children["*"]; ok {
return wild.findMatch(target[1:])
}
} else {
return node.findMatch(target[1:])
}
}
return nil
}