ソースを参照

Added header rewrite logic into dpcore

Toby Chui 9 ヶ月 前
コミット
4241cc4e27

+ 0 - 3
mod/dynamicproxy/Server.go

@@ -39,9 +39,6 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	//Inject headers
-	w.Header().Set("x-proxy-by", "zoraxy/"+h.Parent.Option.HostVersion)
-
 	/*
 		Redirection Routing
 	*/

+ 35 - 1
mod/dynamicproxy/customHeader.go

@@ -7,6 +7,40 @@ package dynamicproxy
 	into the dpcore routing logic
 */
 
-func (ept *ProxyEndpoint) GenerateProxyHeaderArrays() {
+//SplitInboundOutboundHeaders split user defined headers into upstream and downstream headers
+//return upstream header and downstream header key-value pairs
+//if the header is expected to be deleted, the value will be set to empty string
+func (ept *ProxyEndpoint) SplitInboundOutboundHeaders() ([][]string, [][]string) {
+	if len(ept.UserDefinedHeaders) == 0 {
+		//Early return if there are no defined headers
+		return [][]string{}, [][]string{}
+	}
 
+	//Use pre-allocation for faster performance
+	upstreamHeaders := make([][]string, len(ept.UserDefinedHeaders))
+	downstreamHeaders := make([][]string, len(ept.UserDefinedHeaders))
+	upstreamHeaderCounter := 0
+	downstreamHeaderCounter := 0
+
+	//Sort the headers into upstream or downstream
+	for _, customHeader := range ept.UserDefinedHeaders {
+		thisHeaderSet := make([]string, 2)
+		thisHeaderSet[0] = customHeader.Key
+		thisHeaderSet[1] = customHeader.Value
+		if customHeader.IsRemove {
+			//Prevent invalid config
+			thisHeaderSet[1] = ""
+		}
+
+		//Assign to slice
+		if customHeader.Direction == HeaderDirection_ZoraxyToUpstream {
+			upstreamHeaders[upstreamHeaderCounter] = thisHeaderSet
+			upstreamHeaderCounter++
+		} else if customHeader.Direction == HeaderDirection_ZoraxyToDownstream {
+			downstreamHeaders[downstreamHeaderCounter] = thisHeaderSet
+			downstreamHeaderCounter++
+		}
+	}
+
+	return upstreamHeaders, downstreamHeaders
 }

+ 16 - 78
mod/dynamicproxy/dpcore/dpcore.go

@@ -64,6 +64,7 @@ type ResponseRewriteRuleSet struct {
 	PathPrefix        string //Vdir prefix for root, / will be rewrite to this
 	UpstreamHeaders   [][]string
 	DownstreamHeaders [][]string
+	Version           string //Version number of Zoraxy, use for X-Proxy-By
 }
 
 type requestCanceler interface {
@@ -250,82 +251,6 @@ func (p *ReverseProxy) logf(format string, args ...interface{}) {
 	}
 }
 
-func removeHeaders(header http.Header, noCache bool) {
-	// Remove hop-by-hop headers listed in the "Connection" header.
-	if c := header.Get("Connection"); c != "" {
-		for _, f := range strings.Split(c, ",") {
-			if f = strings.TrimSpace(f); f != "" {
-				header.Del(f)
-			}
-		}
-	}
-
-	// Remove hop-by-hop headers
-	for _, h := range hopHeaders {
-		if header.Get(h) != "" {
-			header.Del(h)
-		}
-	}
-
-	//Restore the Upgrade header if any
-	if header.Get("Zr-Origin-Upgrade") != "" {
-		header.Set("Upgrade", header.Get("Zr-Origin-Upgrade"))
-		header.Del("Zr-Origin-Upgrade")
-	}
-
-	//Disable cache if nocache is set
-	if noCache {
-		header.Del("Cache-Control")
-		header.Set("Cache-Control", "no-store")
-	}
-
-	//Hide Go-HTTP-Client UA if the client didnt sent us one
-	if _, ok := header["User-Agent"]; !ok {
-		// If the outbound request doesn't have a User-Agent header set,
-		// don't send the default Go HTTP client User-Agent.
-		header.Set("User-Agent", "")
-	}
-
-}
-
-func addXForwardedForHeader(req *http.Request) {
-	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
-		// If we aren't the first proxy retain prior
-		// X-Forwarded-For information as a comma+space
-		// separated list and fold multiple headers into one.
-		if prior, ok := req.Header["X-Forwarded-For"]; ok {
-			clientIP = strings.Join(prior, ", ") + ", " + clientIP
-		}
-		req.Header.Set("X-Forwarded-For", clientIP)
-		if req.TLS != nil {
-			req.Header.Set("X-Forwarded-Proto", "https")
-		} else {
-			req.Header.Set("X-Forwarded-Proto", "http")
-		}
-
-		if req.Header.Get("X-Real-Ip") == "" {
-			//Check if CF-Connecting-IP header exists
-			CF_Connecting_IP := req.Header.Get("CF-Connecting-IP")
-			Fastly_Client_IP := req.Header.Get("Fastly-Client-IP")
-			if CF_Connecting_IP != "" {
-				//Use CF Connecting IP
-				req.Header.Set("X-Real-Ip", CF_Connecting_IP)
-			} else if Fastly_Client_IP != "" {
-				//Use Fastly Client IP
-				req.Header.Set("X-Real-Ip", Fastly_Client_IP)
-			} else {
-				// Not exists. Fill it in with first entry in X-Forwarded-For
-				ips := strings.Split(clientIP, ",")
-				if len(ips) > 0 {
-					req.Header.Set("X-Real-Ip", strings.TrimSpace(ips[0]))
-				}
-			}
-
-		}
-
-	}
-}
-
 func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr *ResponseRewriteRuleSet) error {
 	transport := p.Transport
 
@@ -364,12 +289,18 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
 	outreq.Header = make(http.Header)
 	copyHeader(outreq.Header, req.Header)
 
-	// Remove hop-by-hop headers listed in the "Connection" header, Remove hop-by-hop headers.
+	// Remove hop-by-hop headers.
 	removeHeaders(outreq.Header, rrr.NoCache)
 
 	// Add X-Forwarded-For Header.
 	addXForwardedForHeader(outreq)
 
+	// Rewrite outbound UA
+	rewriteUserAgent(outreq.Header, "Zoraxy/"+rrr.Version)
+
+	// Add user defined headers (to upstream)
+	injectUserDefinedHeaders(outreq.Header, rrr.UpstreamHeaders)
+
 	res, err := transport.RoundTrip(outreq)
 	if err != nil {
 		if p.Verbal {
@@ -400,13 +331,17 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
 		}
 	}
 
+	//TODO: Figure out a way to proxy for proxmox
 	//if res.StatusCode == 501 || res.StatusCode == 500 {
 	//	fmt.Println(outreq.Proto, outreq.RemoteAddr, outreq.RequestURI)
 	//	fmt.Println(">>>", outreq.Method, res.Header, res.ContentLength, res.StatusCode)
 	//	fmt.Println(outreq.Header, req.Host)
 	//}
 
-	//Custom header rewriter functions
+	//Add debug X-Proxy-By tracker
+	res.Header.Set("x-proxy-by", "zoraxy/"+rrr.Version)
+
+	//Custom Location header rewriter functions
 	if res.Header.Get("Location") != "" {
 		locationRewrite := res.Header.Get("Location")
 		originLocation := res.Header.Get("Location")
@@ -432,6 +367,9 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
 		res.Header.Set("Location", locationRewrite)
 	}
 
+	// Add user defined headers (to downstream)
+	injectUserDefinedHeaders(res.Header, rrr.DownstreamHeaders)
+
 	// Copy header from response to client.
 	copyHeader(rw.Header(), res.Header)
 

+ 121 - 0
mod/dynamicproxy/dpcore/header.go

@@ -0,0 +1,121 @@
+package dpcore
+
+import (
+	"net"
+	"net/http"
+	"strings"
+)
+
+/*
+	Header.go
+
+	This script handles headers rewrite and remove
+	in dpcore.
+
+	Added in Zoraxy v3.0.6 by tobychui
+*/
+
+// removeHeaders Remove hop-by-hop headers listed in the "Connection" header, Remove hop-by-hop headers.
+func removeHeaders(header http.Header, noCache bool) {
+	// Remove hop-by-hop headers listed in the "Connection" header.
+	if c := header.Get("Connection"); c != "" {
+		for _, f := range strings.Split(c, ",") {
+			if f = strings.TrimSpace(f); f != "" {
+				header.Del(f)
+			}
+		}
+	}
+
+	// Remove hop-by-hop headers
+	for _, h := range hopHeaders {
+		if header.Get(h) != "" {
+			header.Del(h)
+		}
+	}
+
+	//Restore the Upgrade header if any
+	if header.Get("Zr-Origin-Upgrade") != "" {
+		header.Set("Upgrade", header.Get("Zr-Origin-Upgrade"))
+		header.Del("Zr-Origin-Upgrade")
+	}
+
+	//Disable cache if nocache is set
+	if noCache {
+		header.Del("Cache-Control")
+		header.Set("Cache-Control", "no-store")
+	}
+
+}
+
+// rewriteUserAgent rewrite the user agent based on incoming request
+func rewriteUserAgent(header http.Header, UA string) {
+	//Hide Go-HTTP-Client UA if the client didnt sent us one
+	if header.Get("User-Agent") == "" {
+		// If the outbound request doesn't have a User-Agent header set,
+		// don't send the default Go HTTP client User-Agent
+		header.Del("User-Agent")
+		header.Set("User-Agent", UA)
+	}
+}
+
+// Add X-Forwarded-For Header and rewrite X-Real-Ip according to sniffing logics
+func addXForwardedForHeader(req *http.Request) {
+	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
+		// If we aren't the first proxy retain prior
+		// X-Forwarded-For information as a comma+space
+		// separated list and fold multiple headers into one.
+		if prior, ok := req.Header["X-Forwarded-For"]; ok {
+			clientIP = strings.Join(prior, ", ") + ", " + clientIP
+		}
+		req.Header.Set("X-Forwarded-For", clientIP)
+		if req.TLS != nil {
+			req.Header.Set("X-Forwarded-Proto", "https")
+		} else {
+			req.Header.Set("X-Forwarded-Proto", "http")
+		}
+
+		if req.Header.Get("X-Real-Ip") == "" {
+			//Check if CF-Connecting-IP header exists
+			CF_Connecting_IP := req.Header.Get("CF-Connecting-IP")
+			Fastly_Client_IP := req.Header.Get("Fastly-Client-IP")
+			if CF_Connecting_IP != "" {
+				//Use CF Connecting IP
+				req.Header.Set("X-Real-Ip", CF_Connecting_IP)
+			} else if Fastly_Client_IP != "" {
+				//Use Fastly Client IP
+				req.Header.Set("X-Real-Ip", Fastly_Client_IP)
+			} else {
+				// Not exists. Fill it in with first entry in X-Forwarded-For
+				ips := strings.Split(clientIP, ",")
+				if len(ips) > 0 {
+					req.Header.Set("X-Real-Ip", strings.TrimSpace(ips[0]))
+				}
+			}
+
+		}
+
+	}
+}
+
+// injectUserDefinedHeaders inject the user headers from slice
+// if a value is empty string, the key will be removed from header.
+// if a key is empty string, the function will return immediately
+func injectUserDefinedHeaders(header http.Header, userHeaders [][]string) {
+	for _, userHeader := range userHeaders {
+		if len(userHeader) == 0 {
+			//End of header slice
+			return
+		}
+		headerKey := userHeader[0]
+		headerValue := userHeader[1]
+		if headerValue == "" {
+			//Remove header from head
+			header.Del(headerKey)
+			continue
+		}
+
+		//Default: Set header value
+		header.Del(headerKey) //Remove header if it already exists
+		header.Set(headerKey, headerValue)
+	}
+}

+ 1 - 0
mod/dynamicproxy/dynamicproxy.go

@@ -142,6 +142,7 @@ func (router *Router) StartProxyService() error {
 							OriginalHost: originalHostHeader,
 							UseTLS:       sep.RequireTLS,
 							PathPrefix:   "",
+							Version:      sep.parent.Option.HostVersion,
 						})
 						return
 					}

+ 19 - 21
mod/dynamicproxy/proxyRequestHandler.go

@@ -146,18 +146,17 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
 	}
 
 	//Build downstream and upstream header rules
-	if len(target.UserDefinedHeaders) > 0 {
-		for _, customHeader := range target.UserDefinedHeaders {
-			r.Header.Set(customHeader.Key, customHeader.Value)
-		}
-	}
+	upstreamHeaders, downstreamHeaders := target.SplitInboundOutboundHeaders()
 
 	err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
-		ProxyDomain:  target.Domain,
-		OriginalHost: originalHostHeader,
-		UseTLS:       target.RequireTLS,
-		NoCache:      h.Parent.Option.NoCache,
-		PathPrefix:   "",
+		ProxyDomain:       target.Domain,
+		OriginalHost:      originalHostHeader,
+		UseTLS:            target.RequireTLS,
+		NoCache:           h.Parent.Option.NoCache,
+		PathPrefix:        "",
+		UpstreamHeaders:   upstreamHeaders,
+		DownstreamHeaders: downstreamHeaders,
+		Version:           target.parent.Option.HostVersion,
 	})
 
 	var dnsError *net.DNSError
@@ -184,13 +183,6 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
 	r.Header.Set("X-Forwarded-Host", r.Host)
 	r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
 
-	//Inject custom headers
-	if len(target.parent.UserDefinedHeaders) > 0 {
-		for _, customHeader := range target.parent.UserDefinedHeaders {
-			r.Header.Set(customHeader.Key, customHeader.Value)
-		}
-	}
-
 	if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" {
 		//Handle WebSocket request. Forward the custom Upgrade header and rewrite origin
 		r.Header.Set("Zr-Origin-Upgrade", "websocket")
@@ -219,11 +211,17 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
 		r.URL, _ = url.Parse(originalHostHeader)
 	}
 
+	//Build downstream and upstream header rules
+	upstreamHeaders, downstreamHeaders := target.parent.SplitInboundOutboundHeaders()
+
 	err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
-		ProxyDomain:  target.Domain,
-		OriginalHost: originalHostHeader,
-		UseTLS:       target.RequireTLS,
-		PathPrefix:   target.MatchingPath,
+		ProxyDomain:       target.Domain,
+		OriginalHost:      originalHostHeader,
+		UseTLS:            target.RequireTLS,
+		PathPrefix:        target.MatchingPath,
+		UpstreamHeaders:   upstreamHeaders,
+		DownstreamHeaders: downstreamHeaders,
+		Version:           target.parent.parent.Option.HostVersion,
 	})
 
 	var dnsError *net.DNSError

+ 0 - 1
mod/dynamicproxy/typedef.go

@@ -86,7 +86,6 @@ type UserDefinedHeader struct {
 	Key       string
 	Value     string
 	IsRemove  bool //Instead of set, remove this key instead
-	IsAppend  bool //Instead of set, append to the current one with "," as seperator
 }
 
 // A Virtual Directory endpoint, provide a subset of ProxyEndpoint for better

+ 0 - 1
reverseproxy.go

@@ -1133,7 +1133,6 @@ func HandleCustomHeaderAdd(w http.ResponseWriter, r *http.Request) {
 		Value:     value,
 		Direction: rewriteDirection,
 		IsRemove:  isRemove,
-		IsAppend:  false,
 	}
 
 	//Create a new custom header object