- commit
- a526333
- parent
- b910e2a
- author
- Eric Bower
- date
- 2024-04-03 14:54:19 +0000 UTC
refactor(pgs): redirects and rewrites
3 files changed,
+81,
-27
+2,
-2
1@@ -179,7 +179,7 @@ func (h *AssetHandler) handle(w http.ResponseWriter, r *http.Request) {
2 status := http.StatusOK
3 attempts := []string{}
4 for _, fp := range routes {
5- if hasProtocol(fp.Filepath) || fp.Status == http.StatusMovedPermanently {
6+ if checkIsRedirect(fp.Status) {
7 h.Logger.Info(
8 "redirecting request",
9 "bucket", h.Bucket.Name,
10@@ -304,7 +304,7 @@ func (h *AssetHandler) handle(w http.ResponseWriter, r *http.Request) {
11 _, err = io.Copy(w, contents)
12
13 if err != nil {
14- h.Logger.Error(err.Error())
15+ h.Logger.Error("io copy", "err", err.Error())
16 }
17 }
18
+16,
-19
1@@ -69,9 +69,8 @@ func expandRoute(projectName, fp string, status int) []*HttpReply {
2 return routes
3 }
4
5-func hasProtocol(url string) bool {
6- isFullUrl := strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://")
7- return isFullUrl
8+func checkIsRedirect(status int) bool {
9+ return status >= 300 && status <= 399
10 }
11
12 func calcRoutes(projectName, fp string, userRedirects []*RedirectRule) []*HttpReply {
13@@ -92,35 +91,33 @@ func calcRoutes(projectName, fp string, userRedirects []*RedirectRule) []*HttpRe
14
15 // user routes
16 for _, redirect := range userRedirects {
17+ // this doesn't make sense and it forbidden
18+ if redirect.From == redirect.To {
19+ continue
20+ }
21 rr := regexp.MustCompile(redirect.From)
22 match := rr.FindStringSubmatch(fp)
23 if len(match) > 0 {
24- userReply := []*HttpReply{}
25- ruleRoute := redirect.To
26-
27- // special case for redirects that include http(s)://
28- isFullUrl := hasProtocol(redirect.To)
29- if !isFullUrl {
30- ruleRoute = shared.GetAssetFileName(&utils.FileEntry{
31- Filepath: filepath.Join(projectName, redirect.To),
32- })
33+ isRedirect := checkIsRedirect(redirect.Status)
34+ if !isRedirect {
35+ // wipe redirect rules to prevent infinite loops
36+ // as such we only support a single hop for user defined redirects
37+ redirectRoutes := calcRoutes(projectName, redirect.To, []*RedirectRule{})
38+ rts = append(rts, redirectRoutes...)
39+ return rts
40 }
41
42+ userReply := []*HttpReply{}
43 var rule *HttpReply
44- if redirect.To != "" && redirect.To != "/" {
45+ if redirect.To != "" {
46 rule = &HttpReply{
47- Filepath: ruleRoute,
48+ Filepath: redirect.To,
49 Status: redirect.Status,
50 Query: redirect.Query,
51 }
52 userReply = append(userReply, rule)
53 }
54
55- if !isFullUrl {
56- expandedRoutes := expandRoute(projectName, redirect.To, redirect.Status)
57- userReply = append(userReply, expandedRoutes...)
58- }
59-
60 if redirect.Force {
61 rts = userReply
62 } else {
+63,
-6
1@@ -1,7 +1,6 @@
2 package pgs
3
4 import (
5- "fmt"
6 "testing"
7
8 "github.com/google/go-cmp/cmp"
9@@ -103,14 +102,14 @@ func TestCalcRoutes(t *testing.T) {
10 []*RedirectRule{
11 {
12 From: "/wow",
13- To: "index.html",
14+ To: "/index.html",
15 Status: 301,
16 },
17 },
18 ),
19 Expected: []*HttpReply{
20 {Filepath: "test/wow.html", Status: 200},
21- {Filepath: "test/index.html", Status: 301},
22+ {Filepath: "/index.html", Status: 301},
23 {Filepath: "/wow/", Status: 301},
24 {Filepath: "test/404.html", Status: 404},
25 },
26@@ -162,7 +161,7 @@ func TestCalcRoutes(t *testing.T) {
27 ),
28 Expected: []*HttpReply{
29 {Filepath: "test/wow/index.html", Status: 200},
30- {Filepath: "test/index.html", Status: 301},
31+ {Filepath: "/", Status: 301},
32 {Filepath: "test/404.html", Status: 404},
33 },
34 },
35@@ -181,7 +180,7 @@ func TestCalcRoutes(t *testing.T) {
36 },
37 ),
38 Expected: []*HttpReply{
39- {Filepath: "test/index.html", Status: 301},
40+ {Filepath: "/", Status: 301},
41 {Filepath: "/wow/", Status: 301},
42 {Filepath: "test/404.html", Status: 404},
43 },
44@@ -225,11 +224,69 @@ func TestCalcRoutes(t *testing.T) {
45 {Filepath: "test/404.html", Status: 404},
46 },
47 },
48+ {
49+ Name: "redirectDirectory",
50+ Actual: calcRoutes(
51+ "public",
52+ "/stata2",
53+ []*RedirectRule{
54+ {
55+ From: "/stata2",
56+ To: "/workshop-stata2",
57+ Status: 301,
58+ },
59+ },
60+ ),
61+ Expected: []*HttpReply{
62+ {Filepath: "public/stata2.html", Status: 200},
63+ {Filepath: "/workshop-stata2", Status: 301},
64+ {Filepath: "/stata2/", Status: 301},
65+ {Filepath: "public/404.html", Status: 404},
66+ },
67+ },
68+ {
69+ Name: "redirectSubDirectory",
70+ Actual: calcRoutes(
71+ "public",
72+ "/folder2",
73+ []*RedirectRule{
74+ {
75+ From: "/folder2",
76+ To: "/folder",
77+ Status: 200,
78+ },
79+ },
80+ ),
81+ Expected: []*HttpReply{
82+ {Filepath: "public/folder2.html", Status: 200},
83+ {Filepath: "public/folder.html", Status: 200},
84+ {Filepath: "/folder/", Status: 301},
85+ {Filepath: "public/404.html", Status: 404},
86+ },
87+ },
88+ {
89+ Name: "redirectFromAndToSame",
90+ Actual: calcRoutes(
91+ "public",
92+ "/folder2",
93+ []*RedirectRule{
94+ {
95+ From: "/folder2",
96+ To: "/folder2",
97+ Status: 200,
98+ },
99+ },
100+ ),
101+ Expected: []*HttpReply{
102+ {Filepath: "public/folder2.html", Status: 200},
103+ {Filepath: "/folder2/", Status: 301},
104+ {Filepath: "public/404.html", Status: 404},
105+ },
106+ },
107 }
108
109 for _, fixture := range fixtures {
110 t.Run(fixture.Name, func(t *testing.T) {
111- fmt.Println(fixture.Actual[0].Filepath)
112 if cmp.Equal(fixture.Actual, fixture.Expected) == false {
113 t.Fatalf(cmp.Diff(fixture.Expected, fixture.Actual))
114 }