fixing sub path_mode

This commit is contained in:
yusing
2024-03-07 04:51:23 +08:00
parent 2f439233ed
commit bee415e22c
12 changed files with 121 additions and 48 deletions

View File

@@ -46,7 +46,7 @@ const (
const (
ProxyPathMode_Forward = "forward"
ProxyPathMode_Sub = "sub" // TODO: implement
ProxyPathMode_Sub = "sub"
ProxyPathMode_RemovedPath = ""
)

View File

@@ -65,20 +65,31 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
initRewrite(pr)
// disable compression
pr.Out.Header.Set("Accept-Encoding", "identity")
// remove path prefix
pr.Out.URL.Path = strings.TrimPrefix(pr.Out.URL.Path, config.Path)
}
route.Proxy.ModifyResponse = func(r *http.Response) error {
contentType, ok := r.Header["Content-Type"]
if !ok || len(contentType) == 0 {
glog.Infof("unknown content type for %s", r.Request.URL.String())
if glog.V(3) {
glog.Infof("[Path sub] unknown content type for %s", r.Request.URL.String())
}
return nil
}
if !strings.HasPrefix(contentType[0], "text/html") {
return nil
// disable cache
r.Header.Set("Cache-Control", "no-store")
var err error = nil
switch {
case strings.HasPrefix(contentType[0], "text/html"):
err = utils.respHTMLSubPath(r, config.Path)
case strings.HasPrefix(contentType[0], "application/javascript"):
err = utils.respJSSubPath(r, config.Path)
default:
glog.V(4).Infof("[Path sub] unknown content type(s): %s", contentType)
}
err := utils.respRemovePath(r, config.Path)
if err != nil {
err = fmt.Errorf("failed to remove path prefix %s: %v", config.Path, err)
err = fmt.Errorf("[Path sub] failed to remove path prefix %s: %v", config.Path, err)
r.Status = err.Error()
r.StatusCode = http.StatusInternalServerError
}
@@ -96,7 +107,7 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
rewrite(pr)
r := pr.In
glog.Infof("[Request] %s %s%s", r.Method, r.Host, r.URL.Path)
glog.V(4).InfoDepthf(1, "Headers: %v", r.Header)
glog.V(5).InfoDepthf(1, "Headers: %v", r.Header)
}
} else {
route.Proxy.Rewrite = rewrite
@@ -141,7 +152,6 @@ func httpProxyHandler(w http.ResponseWriter, r *http.Request) {
r.URL.Path,
err,
)
glog.Error(err)
http.Error(w, err.Error(), http.StatusNotFound)
return
}

View File

@@ -4,6 +4,7 @@ import (
"flag"
"net/http"
"runtime"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@@ -51,6 +52,12 @@ func main() {
}
}()
go func() {
for range time.Tick(100 * time.Millisecond) {
glog.Flush()
}
}()
mux := http.NewServeMux()
mux.HandleFunc("/", httpProxyHandler)

View File

@@ -6,10 +6,14 @@ import (
"io"
"net"
"net/http"
"path"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
"github.com/golang/glog"
xhtml "golang.org/x/net/html"
)
@@ -79,7 +83,7 @@ func (*Utils) healthCheckHttp(targetUrl string) error {
return err
}
func (*Utils) healthCheckStream(scheme string, host string) error {
func (*Utils) healthCheckStream(scheme, host string) error {
conn, err := net.DialTimeout(scheme, host, 5*time.Second)
if err != nil {
return err
@@ -93,25 +97,49 @@ func (*Utils) snakeToCamel(s string) string {
return strings.ReplaceAll(toHyphenCamel, "-", "")
}
func htmlNodesSubPath(node *xhtml.Node, path string) {
if node.Type == xhtml.ElementNode {
for _, attr := range node.Attr {
switch attr.Key {
case "src": // img, script, etc.
case "href": // link
case "action": // form
if strings.HasPrefix(attr.Val, path) {
attr.Val = strings.Replace(attr.Val, path, "", 1)
}
}
}
}
for c := node.FirstChild; c != nil; c = c.NextSibling {
htmlNodesSubPath(c, path)
func tryAppendPathPrefixImpl(pOrig, pAppend string) string {
switch {
case strings.Contains(pOrig, "://"):
return pOrig
case pOrig == "", pOrig == "#", pOrig == "/":
return pAppend
case filepath.IsLocal(pOrig) && !strings.HasPrefix(pOrig, pAppend):
return path.Join(pAppend, pOrig)
default:
return pOrig
}
}
func (*Utils) respRemovePath(r *http.Response, path string) error {
var tryAppendPathPrefix func(string, string) string
var _ = func() int {
if glog.V(4) {
tryAppendPathPrefix = func(s1, s2 string) string {
replaced := tryAppendPathPrefixImpl(s1, s2)
glog.Infof("[Path sub] %s -> %s", s1, replaced)
return replaced
}
} else {
tryAppendPathPrefix = tryAppendPathPrefixImpl
}
return 1
}()
func htmlNodesSubPath(n *xhtml.Node, p string) {
if n.Type == xhtml.ElementNode {
for i, attr := range n.Attr {
switch attr.Key {
case "src", "href", "action": // img, script, link, form etc.
n.Attr[i].Val = tryAppendPathPrefix(attr.Val, p)
}
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
htmlNodesSubPath(c, p)
}
}
func (*Utils) respHTMLSubPath(r *http.Response, p string) error {
// remove all path prefix from relative path in script, img, a, ...
doc, err := xhtml.Parse(r.Body)
@@ -119,11 +147,14 @@ func (*Utils) respRemovePath(r *http.Response, path string) error {
return err
}
htmlNodesSubPath(doc, path)
if p[0] == '/' {
p = p[1:]
}
htmlNodesSubPath(doc, p)
var buf bytes.Buffer
err = xhtml.Render(&buf, doc)
if err != nil {
return err
}
@@ -132,3 +163,29 @@ func (*Utils) respRemovePath(r *http.Response, path string) error {
return nil
}
func (*Utils) respJSSubPath(r *http.Response, p string) error {
var buf bytes.Buffer
_, err := buf.ReadFrom(r.Body)
if err != nil {
return err
}
if p[0] == '/' {
p = p[1:]
}
js := buf.String()
re := regexp.MustCompile(`fetch\(["'].+["']\)`)
replace := func(match string) string {
match = match[7 : len(match)-2]
replaced := tryAppendPathPrefix(match, p)
return fmt.Sprintf(`fetch(%q)`, replaced)
}
js = re.ReplaceAllStringFunc(js, replace)
r.Body = io.NopCloser(strings.NewReader(js))
return nil
}