94 lines
2.4 KiB
Go
94 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
func getReverseProxy(target string, skipVerify bool) *httputil.ReverseProxy {
|
|
targetURL, err := url.Parse(target)
|
|
if err != nil {
|
|
log.Printf("Error parsing target URL %s: %v", target, err)
|
|
return nil
|
|
}
|
|
|
|
director := func(req *http.Request) {
|
|
req.URL.Scheme = targetURL.Scheme
|
|
req.URL.Host = targetURL.Host
|
|
req.Host = targetURL.Host // Set Host header to match target
|
|
|
|
// Preserve original headers
|
|
for k, v := range req.Header {
|
|
if k != "Host" { // Host is set above
|
|
req.Header[k] = v
|
|
}
|
|
}
|
|
|
|
// Ensure the full path is preserved
|
|
if targetURL.Path != "" {
|
|
req.URL.Path = strings.TrimPrefix(req.URL.Path, "/") // Avoid double slashes
|
|
req.URL.Path = singleJoin(targetURL.Path, req.URL.Path)
|
|
}
|
|
}
|
|
|
|
transport := &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: skipVerify},
|
|
}
|
|
|
|
return &httputil.ReverseProxy{
|
|
Director: director,
|
|
Transport: transport,
|
|
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
|
log.Printf("Proxy error for %s: %v", r.Host, err)
|
|
http.Error(w, "Proxy error", http.StatusBadGateway)
|
|
},
|
|
}
|
|
}
|
|
|
|
// singleJoin ensures a single slash between path segments
|
|
func singleJoin(prefix, suffix string) string {
|
|
prefix = strings.TrimSuffix(prefix, "/")
|
|
suffix = strings.TrimPrefix(suffix, "/")
|
|
return prefix + "/" + suffix
|
|
}
|
|
|
|
func handler(w http.ResponseWriter, r *http.Request) {
|
|
configMux.RLock()
|
|
defer configMux.RUnlock()
|
|
|
|
target, exists := config.Routes[r.Host]
|
|
skipVerify := config.TrustTarget[r.Host]
|
|
noHTTPSRedirect := config.NoHTTPSRedirect[r.Host]
|
|
|
|
if !exists {
|
|
if target, exists = config.Routes["*"]; !exists {
|
|
http.Error(w, "Host not configured", http.StatusNotFound)
|
|
return
|
|
}
|
|
skipVerify = config.TrustTarget["*"]
|
|
noHTTPSRedirect = config.NoHTTPSRedirect["*"]
|
|
}
|
|
|
|
// Check if request is HTTP and target is HTTPS
|
|
isHTTPS := target[:5] == "https"
|
|
isHTTPReq := r.TLS == nil // r.TLS is nil for HTTP, non-nil for HTTPS
|
|
|
|
if isHTTPReq && isHTTPS && !noHTTPSRedirect {
|
|
// Redirect to HTTPS version of the host
|
|
redirectURL := "https://" + r.Host + r.RequestURI
|
|
http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
|
|
return
|
|
}
|
|
|
|
proxy := getReverseProxy(target, skipVerify)
|
|
if proxy == nil {
|
|
http.Error(w, "Invalid target configuration", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
proxy.ServeHTTP(w, r)
|
|
}
|