Skip to content

Commit

Permalink
feat(pgs): rewrite support
Browse files Browse the repository at this point in the history
feat(pgs): rewrite proxy api support
  • Loading branch information
neurosnap committed Jul 13, 2024
1 parent 2f441a5 commit 07f2211
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 17 deletions.
15 changes: 15 additions & 0 deletions pgs/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,21 @@ func (h *AssetHandler) handle(w http.ResponseWriter, r *http.Request) {
)
http.Redirect(w, r, fp.Filepath, fp.Status)
return
} else if hasProtocol(fp.Filepath) {
// fetch content from url and serve it
resp, err := http.Get(fp.Filepath)
if err != nil {
http.Error(w, "404 not found", http.StatusNotFound)
return
}

w.Header().Set("content-type", resp.Header.Get("content-type"))
w.WriteHeader(status)
_, err = io.Copy(w, resp.Body)
if err != nil {
h.Logger.Error("io copy", "err", err.Error())
}
return
}

attempts = append(attempts, fp.Filepath)
Expand Down
90 changes: 79 additions & 11 deletions pgs/cal_route.go → pgs/calc_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pgs
import (
"fmt"
"net/http"
"net/url"
"path/filepath"
"regexp"
"strings"
Expand Down Expand Up @@ -73,6 +74,66 @@ func checkIsRedirect(status int) bool {
return status >= 300 && status <= 399
}

func correlatePlaceholder(orig, pattern string) string {
origList := strings.Split(orig, "/")
patternList := strings.Split(pattern, "/")
nextList := []string{}
for idx, item := range patternList {
if strings.HasPrefix(item, ":") {
nextList = append(nextList, origList[idx])
} else if item == origList[idx] {
nextList = append(nextList, origList[idx])
}
}
return filepath.Join(nextList...)
}

func genRedirectRoute(actual string, fromStr string, to string) string {
if to == "/" {
return to
}
actualList := strings.Split(actual, "/")
fromList := strings.Split(fromStr, "/")
prefix := ""
var toList []string
if hasProtocol(to) {
u, _ := url.Parse(to)
if u.Path == "" {
return to
}
toList = strings.Split(u.Path, "/")
prefix = u.Scheme + "://" + u.Host
} else {
toList = strings.Split(to, "/")
}

mapper := map[string]string{}
for idx, item := range fromList {
if strings.HasPrefix(item, ":") {
mapper[item] = actualList[idx]
}
if item == "*" {
mapper[":splat"] = actualList[idx]
}
}

fin := []string{"/"}

for _, item := range toList {
if mapper[item] != "" {
fin = append(fin, mapper[item])
} else {
fin = append(fin, item)
}
}

result := prefix + filepath.Join(fin...)
if strings.HasSuffix(to, "/") {
result += "/"
}
return result
}

func calcRoutes(projectName, fp string, userRedirects []*RedirectRule) []*HttpReply {
rts := []*HttpReply{}
// add route as-is without expansion
Expand All @@ -87,32 +148,33 @@ func calcRoutes(projectName, fp string, userRedirects []*RedirectRule) []*HttpRe

// user routes
for _, redirect := range userRedirects {
// this doesn't make sense and it forbidden
// this doesn't make sense so it is forbidden
if redirect.From == redirect.To {
continue
}

from := redirect.From
if !strings.HasSuffix(redirect.From, "*") {
from = strings.TrimSuffix(redirect.From, "/") + "/?"
}
rr := regexp.MustCompile(from)
// hack: make suffix `/` optional when matching
from := filepath.Clean(redirect.From)
fromMatcher := correlatePlaceholder(fp, from)
rr := regexp.MustCompile(fromMatcher)
match := rr.FindStringSubmatch(fp)
if len(match) > 0 {
isRedirect := checkIsRedirect(redirect.Status)
if !isRedirect {
if !isRedirect && !hasProtocol(redirect.To) {
route := genRedirectRoute(fp, from, redirect.To)
// wipe redirect rules to prevent infinite loops
// as such we only support a single hop for user defined redirects
redirectRoutes := calcRoutes(projectName, redirect.To, []*RedirectRule{})
redirectRoutes := calcRoutes(projectName, route, []*RedirectRule{})
rts = append(rts, redirectRoutes...)
return rts
}

route := genRedirectRoute(fp, from, redirect.To)
userReply := []*HttpReply{}
var rule *HttpReply
if redirect.To != "" {
rule = &HttpReply{
Filepath: redirect.To,
Filepath: route,
Status: redirect.Status,
Query: redirect.Query,
}
Expand All @@ -124,8 +186,14 @@ func calcRoutes(projectName, fp string, userRedirects []*RedirectRule) []*HttpRe
} else {
rts = append(rts, userReply...)
}
// quit after first match
break

if hasProtocol(redirect.To) {
// redirecting to another site so we should bail early
return rts
} else {
// quit after first match
break
}
}
}

Expand Down
46 changes: 40 additions & 6 deletions pgs/calc_route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ func TestCalcRoutes(t *testing.T) {
{Filepath: "test/tester1", Status: 200},
{Filepath: "test/tester1.html", Status: 200},
{Filepath: "https://pico.sh", Status: 301},
{Filepath: "/tester1/", Status: 301},
{Filepath: "test/404.html", Status: 404},
},
},
{
Expand Down Expand Up @@ -211,8 +209,6 @@ func TestCalcRoutes(t *testing.T) {
Expected: []*HttpReply{
{Filepath: "test/wow.html", Status: 200},
{Filepath: "https://pico.sh", Status: 301},
{Filepath: "/wow.html/", Status: 301},
{Filepath: "test/404.html", Status: 404},
},
},
{
Expand All @@ -232,8 +228,6 @@ func TestCalcRoutes(t *testing.T) {
{Filepath: "test/wow", Status: 200},
{Filepath: "test/wow.html", Status: 200},
{Filepath: "https://pico.sh", Status: 301},
{Filepath: "/wow/", Status: 301},
{Filepath: "test/404.html", Status: 404},
},
},
{
Expand Down Expand Up @@ -353,6 +347,46 @@ func TestCalcRoutes(t *testing.T) {
{Filepath: "public/404.html", Status: 404},
},
},
{
Name: "redirect-to-another-pgs-site",
Actual: calcRoutes(
"public",
"/my-site/index.html",
[]*RedirectRule{
{
From: "/my-site/*",
To: "https://my-other-site.pgs.sh/:splat",
Status: 200,
},
},
),
Expected: []*HttpReply{
{Filepath: "public/my-site/index.html", Status: 200},
{Filepath: "https://my-other-site.pgs.sh/index.html", Status: 200},
},
},
{
Name: "redirect-placeholders",
Actual: calcRoutes(
"public",
"/news/02/12/2004/my-story",
[]*RedirectRule{
{
From: "/news/:month/:date/:year/*",
To: "/blog/:year/:month/:date/:splat",
Status: 200,
},
},
),
Expected: []*HttpReply{
{Filepath: "public/news/02/12/2004/my-story", Status: 200},
{Filepath: "public/news/02/12/2004/my-story.html", Status: 200},
{Filepath: "public/blog/2004/02/12/my-story", Status: 200},
{Filepath: "public/blog/2004/02/12/my-story.html", Status: 200},
{Filepath: "/blog/2004/02/12/my-story/", Status: 301},
{Filepath: "public/404.html", Status: 404},
},
},
}

for _, fixture := range fixtures {
Expand Down

0 comments on commit 07f2211

Please sign in to comment.