From e77f947d1d6874ea3c95d3a3a759ce5fc7ed43fe Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Fri, 10 Oct 2025 14:43:38 +0800 Subject: [PATCH] Added loopback proxy support - Added support for shortcut loopback setup in local setups --- src/mod/dynamicproxy/Server.go | 1 - src/mod/dynamicproxy/dpcore/dpcore.go | 10 ++++++- src/mod/dynamicproxy/proxyRequestHandler.go | 33 +++++++++++++++++---- src/mod/uptime/uptime.go | 1 - 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/mod/dynamicproxy/Server.go b/src/mod/dynamicproxy/Server.go index e0b16bb..a9b1aa7 100644 --- a/src/mod/dynamicproxy/Server.go +++ b/src/mod/dynamicproxy/Server.go @@ -92,7 +92,6 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } //Plugin routing - if h.Parent.Option.PluginManager != nil && h.Parent.Option.PluginManager.HandleRoute(w, r, sep.Tags) { //Request handled by subroute return diff --git a/src/mod/dynamicproxy/dpcore/dpcore.go b/src/mod/dynamicproxy/dpcore/dpcore.go index 9b6157e..dcdfbf2 100644 --- a/src/mod/dynamicproxy/dpcore/dpcore.go +++ b/src/mod/dynamicproxy/dpcore/dpcore.go @@ -438,7 +438,15 @@ func (p *ReverseProxy) ProxyHTTPS(rw http.ResponseWriter, req *http.Request) (in if !strings.Contains(host, ":") { host += ":443" } - serverName := req.URL.Hostname() + serverName := "" + //if p.Transport != nil { + // if tr, ok := p.Transport.(*http.Transport); ok && tr.TLSClientConfig != nil && tr.TLSClientConfig.ServerName != "" { + // serverName = tr.TLSClientConfig.ServerName + // } + //} + if serverName == "" { + serverName = req.URL.Hostname() + } // Connect with SNI offload tlsConfig := &tls.Config{ diff --git a/src/mod/dynamicproxy/proxyRequestHandler.go b/src/mod/dynamicproxy/proxyRequestHandler.go index c1bb15e..757bc55 100644 --- a/src/mod/dynamicproxy/proxyRequestHandler.go +++ b/src/mod/dynamicproxy/proxyRequestHandler.go @@ -12,6 +12,7 @@ import ( "strings" "imuslab.com/zoraxy/mod/dynamicproxy/dpcore" + "imuslab.com/zoraxy/mod/dynamicproxy/loadbalance" "imuslab.com/zoraxy/mod/dynamicproxy/rewrite" "imuslab.com/zoraxy/mod/netutils" "imuslab.com/zoraxy/mod/statistic" @@ -95,27 +96,41 @@ func (router *Router) GetProxyEndpointFromHostname(hostname string) *ProxyEndpoi return targetSubdomainEndpoint } -// Clearn URL Path (without the http:// part) replaces // in a URL to / -func (router *Router) clearnURL(targetUrlOPath string) string { - return strings.ReplaceAll(targetUrlOPath, "//", "/") -} - // Rewrite URL rewrite the prefix part of a virtual directory URL with / func (router *Router) rewriteURL(rooturl string, requestURL string) string { rewrittenURL := requestURL rewrittenURL = strings.TrimPrefix(rewrittenURL, strings.TrimSuffix(rooturl, "/")) if strings.Contains(rewrittenURL, "//") { - rewrittenURL = router.clearnURL(rewrittenURL) + rewrittenURL = strings.ReplaceAll(rewrittenURL, "//", "/") } return rewrittenURL } +// upstreamHostSwap check if this loopback to one of the proxy rule in the system. If yes, do a shortcut target swap +// this prevents unnecessary external DNS lookup and connection, return true if swapped and request is already handled +// by the loopback handler. Only continue if return is false +func (h *ProxyHandler) upstreamHostSwap(w http.ResponseWriter, r *http.Request, selectedUpstream *loadbalance.Upstream) bool { + upstreamHostanme := selectedUpstream.OriginIpOrDomain + if strings.Contains(upstreamHostanme, ":") { + upstreamHostanme = strings.Split(upstreamHostanme, ":")[0] + } + loopbackProxyEndpoint := h.Parent.GetProxyEndpointFromHostname(upstreamHostanme) + if loopbackProxyEndpoint != nil { + //This is a loopback request. Swap the target to the loopback target + //h.Parent.Option.Logger.PrintAndLog("proxy", "Detected a loopback request to self. Swap the target to "+loopbackProxyEndpoint.RootOrMatchingDomain, nil) + h.hostRequest(w, r, loopbackProxyEndpoint) + return true + } + return false +} + // Handle host request func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) { r.Header.Set("X-Forwarded-Host", r.Host) r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID) reqHostname := r.Host + /* Load balancing */ selectedUpstream, err := h.Parent.loadBalancer.GetRequestUpstreamTarget(w, r, target.ActiveOrigins, target.UseStickySession) if err != nil { @@ -125,6 +140,12 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe return } + /* Upstream Host Swap (use to detect loopback to self) */ + if h.upstreamHostSwap(w, r, selectedUpstream) { + //Request handled by the loopback handler + return + } + /* WebSocket automatic proxy */ requestURL := r.URL.String() if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" { diff --git a/src/mod/uptime/uptime.go b/src/mod/uptime/uptime.go index 7d31da0..1cf897c 100644 --- a/src/mod/uptime/uptime.go +++ b/src/mod/uptime/uptime.go @@ -211,7 +211,6 @@ func getWebsiteStatus(url string) (int, error) { } resp, err := client.Do(req) - //resp, err := client.Get(url) if err != nil { //Try replace the http with https and vise versa rewriteURL := ""