forked from txtdirect/txtdirect
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfallback.go
136 lines (112 loc) · 3.72 KB
/
fallback.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
125
126
127
128
129
130
131
132
133
134
135
136
package txtdirect
import (
"fmt"
"log"
"net/http"
"strconv"
"strings"
)
// Fallback keeps the data necessary for the fallback flow
type Fallback struct {
rw http.ResponseWriter
request *http.Request
config Config
records []Record
pathRecord Record
lastRecord Record
fallbackType string
code int
// Which record to use for fallback. (last record or path record)
last bool
aggregation bool
}
// fallback redirects the request to the given fallback address
// and if it's not provided it will check txtdirect config for
// default fallback address
func fallback(w http.ResponseWriter, r *http.Request, fallbackType string, code int, c Config) {
if code == http.StatusMovedPermanently {
w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d", Status301CacheAge))
}
w.Header().Add("Status-Code", strconv.Itoa(code))
f := Fallback{
rw: w,
request: r,
config: c,
fallbackType: fallbackType,
code: code,
}
if fallbackType != "global" {
// Fetch records from request's context and set the []record type on them
f.fetchRecords()
if !f.lastRecordFallback() {
if !f.pathFallback() {
// If non of the above cases applied on the record, jump into global redirects
f.globalFallbacks(f.lastRecord.Type)
}
}
log.Printf("[txtdirect]: %s > %s", r.Host+r.URL.Path, w.Header().Get("Location"))
return
}
f.globalFallbacks("")
log.Printf("[txtdirect]: %s > %s", r.Host+r.URL.Path, w.Header().Get("Location"))
}
func (f *Fallback) globalFallbacks(recordType string) {
if contains(f.config.Enable, "www") {
s := strings.Join([]string{defaultProtocol, "://", defaultSub, ".", f.request.URL.Host}, "")
http.Redirect(f.rw, f.request, s, f.code)
} else if f.config.Redirect != "" {
f.rw.Header().Set("Status-Code", strconv.Itoa(http.StatusMovedPermanently))
http.Redirect(f.rw, f.request, f.config.Redirect, http.StatusMovedPermanently)
f.code = http.StatusMovedPermanently
} else {
http.NotFound(f.rw, f.request)
}
}
func (f *Fallback) fetchRecords() {
f.records = f.request.Context().Value("records").([]Record)
// Note: This condition should get changed when we support more record aggregations.
if len(f.records) >= 2 {
f.pathRecord = f.records[len(f.records)-2]
}
f.lastRecord = f.records[len(f.records)-1]
}
// Checks the last record's `to=`, `website=`, and `root=` field to fallback
// Returns false if it can't find an endpoint to fallback to
func (f *Fallback) lastRecordFallback() bool {
// Redirect to first record's `to=` field
if f.fallbackType == "to" && f.lastRecord.To != "" {
http.Redirect(f.rw, f.request, f.lastRecord.To, f.code)
return true
}
// Redirect to first record's `website=` field
if f.fallbackType == "website" && f.lastRecord.Website != "" {
http.Redirect(f.rw, f.request, f.lastRecord.Website, f.code)
return true
}
// Redirect to first record's `root=` field
if f.fallbackType == "root" && f.lastRecord.Root != "" {
http.Redirect(f.rw, f.request, f.lastRecord.Root, f.code)
return true
}
return false
}
// Checks the path record's `to=`, `website=`, and `root=` field to fallback
// Returns false if it can't find an endpoint to fallback to
func (f *Fallback) pathFallback() bool {
// Redirect to path record's `website=` field
if f.fallbackType == "website" && f.pathRecord.Website != "" {
http.Redirect(f.rw, f.request, f.pathRecord.Website, f.code)
return true
}
// Redirect to path record's `root=` field
if f.fallbackType == "root" && f.pathRecord.Root != "" {
http.Redirect(f.rw, f.request, f.pathRecord.Root, f.code)
return true
}
// Redirect to path record's `to=` field
if f.pathRecord.To != "" {
http.Redirect(f.rw, f.request, f.pathRecord.To, f.code)
return true
}
return false
}