package reverse_proxy import ( "net/http" "net/http/httputil" "net/url" "strings" conf "zeevdiukman.com/zprox/internal/config" ) // var config = conf.New() type ReverseProxy struct { Name string Host string Httputil *httputil.ReverseProxy } func New(host string) *ReverseProxy { return &ReverseProxy{ Host: host, Httputil: &httputil.ReverseProxy{ Director: DefaultDirector(host), }, } } type Data struct { Name string Value any } type DirectorFunc func(req *http.Request, data []Data) (*http.Request, []Data) func GetData[T any](key string, data []Data) (res T) { for _, v := range data { if v.Name == key { res = v.Value.(T) } } return res } func (rp *ReverseProxy) DefaultDirectorFunc(d []Data, fn DirectorFunc) func(*http.Request) { return func(r *http.Request) { host := "" req, data := fn(r, d) proxyData := GetData[conf.ReverseProxy]("proxy_data", data) host = proxyData.Host target, _ := url.Parse(host) targetQuery := target.RawQuery req.URL.Scheme = target.Scheme req.URL.Host = target.Host req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) if targetQuery == "" || req.URL.RawQuery == "" { req.URL.RawQuery = targetQuery + req.URL.RawQuery } else { req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery } } } func DefaultDirector(host string) func(*http.Request) { return func(req *http.Request) { target, _ := url.Parse(host) targetQuery := target.RawQuery req.URL.Scheme = target.Scheme req.URL.Host = target.Host req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) if targetQuery == "" || req.URL.RawQuery == "" { req.URL.RawQuery = targetQuery + req.URL.RawQuery } else { req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery } } } func joinURLPath(a, b *url.URL) (path, rawpath string) { if a.RawPath == "" && b.RawPath == "" { return SingleJoiningSlash(a.Path, b.Path), "" } // Same as singleJoiningSlash, but uses EscapedPath to determine // whether a slash should be added apath := a.EscapedPath() bpath := b.EscapedPath() aslash := strings.HasSuffix(apath, "/") bslash := strings.HasPrefix(bpath, "/") switch { case aslash && bslash: return a.Path + b.Path[1:], apath + bpath[1:] case !aslash && !bslash: return a.Path + "/" + b.Path, apath + "/" + bpath } return a.Path + b.Path, apath + bpath } func SingleJoiningSlash(a, b string) string { aslash := strings.HasSuffix(a, "/") bslash := strings.HasPrefix(b, "/") switch { case aslash && bslash: return a + b[1:] case !aslash && !bslash: return a + "/" + b } return a + b }