Cosmos-Server/src/proxy/routeTo.go

170 lines
4.4 KiB
Go
Raw Normal View History

2023-02-26 22:26:09 +00:00
package proxy
import (
"net/http"
"net/http/httputil"
2023-02-26 22:26:09 +00:00
"net/url"
2023-06-20 18:53:25 +00:00
"strings"
2023-05-27 12:58:33 +00:00
"crypto/tls"
2023-03-31 19:19:38 +00:00
spa "github.com/roberthodgen/spa-server"
2023-03-25 20:15:00 +00:00
"github.com/azukaar/cosmos-server/src/utils"
2023-02-26 22:26:09 +00:00
)
2023-06-20 18:53:25 +00:00
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
}
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
}
2023-02-26 22:26:09 +00:00
// NewProxy takes target host and creates a reverse proxy
2023-09-22 17:10:43 +00:00
func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardHeader bool, DisableHeaderHardening bool, CORSOrigin string, route utils.ProxyRouteConfig) (*httputil.ReverseProxy, error) {
2023-02-26 22:26:09 +00:00
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
}
proxy := httputil.NewSingleHostReverseProxy(url)
2023-06-20 18:42:17 +00:00
proxy.Director = func(req *http.Request) {
2023-06-23 16:02:24 +00:00
originalScheme := "http"
if utils.IsHTTPS {
originalScheme = "https"
}
2023-06-20 18:53:25 +00:00
urlQuery := url.RawQuery
req.URL.Scheme = url.Scheme
req.URL.Host = url.Host
req.URL.Path, req.URL.RawPath = joinURLPath(url, req.URL)
if urlQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = urlQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = urlQuery + "&" + req.URL.RawQuery
}
2023-06-23 16:02:24 +00:00
req.Header.Set("X-Forwarded-Proto", originalScheme)
2023-07-12 21:15:03 +00:00
2023-08-11 09:18:24 +00:00
if(originalScheme == "https") {
req.Header.Set("X-Forwarded-Ssl", "on")
}
2023-09-22 17:10:43 +00:00
req.Header.Del("X-Origin-Host")
req.Header.Del("X-Forwarded-Host")
req.Header.Del("X-Forwarded-For")
req.Header.Del("X-Real-Ip")
2023-10-06 15:58:30 +00:00
// hide hostname (dangerous)
// req.Header.Del("Host")
2023-09-22 17:10:43 +00:00
hostname := utils.GetMainConfig().HTTPConfig.Hostname
if route.Host != "" && route.UseHost {
hostname = route.Host
}
if route.UsePathPrefix {
hostname = hostname + route.PathPrefix
2023-08-26 13:38:05 +00:00
}
2023-10-06 15:58:30 +00:00
hostDest := hostname
if route.OverwriteHostHeader != "" {
hostDest = route.OverwriteHostHeader
}
// hide hostname (dangerous)
// req.Header.Set("Host", url.Host)
// req.Host = url.Host
req.Header.Set("Host", hostDest)
req.Host = hostDest
2023-08-26 13:38:05 +00:00
if VerboseForwardHeader {
2023-10-06 15:58:30 +00:00
req.Header.Set("X-Origin-Host", hostDest)
req.Header.Set("X-Forwarded-Host", hostDest)
2023-08-10 15:53:12 +00:00
req.Header.Set("X-Forwarded-For", utils.GetClientIP(req))
req.Header.Set("X-Real-IP", utils.GetClientIP(req))
2023-10-06 15:58:30 +00:00
}
2023-06-20 18:42:17 +00:00
}
2023-02-26 22:26:09 +00:00
2023-05-27 12:58:33 +00:00
if AcceptInsecureHTTPSTarget && url.Scheme == "https" {
proxy.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
2023-02-26 22:26:09 +00:00
proxy.ModifyResponse = func(resp *http.Response) error {
2023-03-10 20:59:56 +00:00
utils.Debug("Response from backend: " + resp.Status)
utils.Debug("URL was " + resp.Request.URL.String())
2023-08-26 13:50:07 +00:00
if CORSOrigin != "" {
2023-08-10 15:53:12 +00:00
resp.Header.Del("Access-Control-Allow-Origin")
resp.Header.Del("Access-Control-Allow-Credentials")
2023-08-26 13:50:07 +00:00
}
if !DisableHeaderHardening {
2023-08-10 15:53:12 +00:00
resp.Header.Del("Strict-Transport-Security")
resp.Header.Del("X-Content-Type-Options")
resp.Header.Del("Content-Security-Policy")
resp.Header.Del("X-XSS-Protection")
}
2023-02-26 22:26:09 +00:00
return nil
}
return proxy, nil
}
func RouteTo(route utils.ProxyRouteConfig) http.Handler {
2023-02-26 22:26:09 +00:00
// initialize a reverse proxy and pass the actual backend server url here
2023-03-31 19:19:38 +00:00
destination := route.Target
routeType := route.Mode
if(routeType == "SERVAPP" || routeType == "PROXY") {
2023-09-22 17:10:43 +00:00
proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget, route.VerboseForwardHeader, route.DisableHeaderHardening, route.CORSOrigin, route)
2023-03-31 19:19:38 +00:00
if err != nil {
utils.Error("Create Route", err)
}
// create a handler function which uses the reverse proxy
return proxy
} else if (routeType == "STATIC") {
return http.FileServer(http.Dir(destination))
} else if (routeType == "SPA") {
return spa.SpaHandler(destination, "index.html")
} else if(routeType == "REDIRECT") {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, destination, 302)
})
} else {
utils.Error("Invalid route type", nil)
return nil
}
2023-02-26 22:26:09 +00:00
}