Files
GoLangProxy/golangproxy/proxy/proxy.go
2025-03-02 17:14:05 +00:00

97 lines
3.0 KiB
Go

package proxy
import (
"context"
"crypto/tls"
"net"
"net/http"
"net/http/httputil"
"net/url"
"golangproxy/logger"
)
// Route holds proxy configuration for a specific host
type Route struct {
Proxy *httputil.ReverseProxy // The reverse proxy instance
Handler http.Handler // Custom handler wrapping the proxy
NoHTTPSRedirect bool // Disable HTTP to HTTPS redirect
Target string // Target URL for proxying
}
// CreateRoute initializes a reverse proxy for a target with trust settings
func CreateRoute(target string, trustInvalidCert bool) *Route {
url, _ := url.Parse(target)
proxy := httputil.NewSingleHostReverseProxy(url)
if url.Scheme == "https" {
proxy.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: trustInvalidCert},
}
}
// Modify the Director based on whether the target is an IP or hostname
originalDirector := proxy.Director
proxy.Director = func(req *http.Request) {
originalDirector(req)
if isIPTarget(url.Hostname()) {
// For IP targets, preserve the incoming Host header (e.g., main.example.com)
// This ensures session cookies match the client's requested domain
} else {
// For hostname targets, set Host to the target's hostname (e.g., example.com)
req.Host = url.Host
}
req.Header.Set("X-Forwarded-For", req.RemoteAddr)
req.Header.Set("X-Forwarded-Host", req.Host)
req.Header.Set("X-Forwarded-Proto", url.Scheme)
if req.Header.Get("User-Agent") == "" {
req.Header.Set("User-Agent", "GoLangProxy")
}
//logger.Logger.Printf("Proxying to %s - Headers: %v, Cookies: %v", target, req.Header, req.Cookies())
}
// Create a custom handler to wrap the proxy and filter context canceled errors
handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rwWrapper := &responseWriterWrapper{ResponseWriter: rw}
proxy.ServeHTTP(rwWrapper, req)
if err := req.Context().Err(); err != nil && err != context.Canceled {
logger.Logger.Printf("Proxy error for %s: %v", target, err)
}
//logger.Logger.Printf("Response from %s - Headers: %v, Status: %d", target, rwWrapper.Header(), rwWrapper.status)
})
return &Route{
Proxy: proxy,
Handler: handler,
Target: target,
}
}
// isIPTarget checks if the target hostname is an IP address
func isIPTarget(host string) bool {
// Split host and port if a port is present (e.g., "10.100.111.254:4444")
hostname, _, err := net.SplitHostPort(host)
if err != nil {
// If there's no port (or an error splitting), use the original host
hostname = host
}
return net.ParseIP(hostname) != nil
}
// responseWriterWrapper captures response status and headers
type responseWriterWrapper struct {
http.ResponseWriter
status int
}
func (rw *responseWriterWrapper) WriteHeader(status int) {
rw.status = status
rw.ResponseWriter.WriteHeader(status)
}
func (rw *responseWriterWrapper) Write(b []byte) (int, error) {
if rw.status == 0 {
rw.status = http.StatusOK
}
return rw.ResponseWriter.Write(b)
}