violet/target/route.go

164 lines
4.1 KiB
Go
Raw Normal View History

package target
import (
2023-04-21 03:21:46 +01:00
"fmt"
"github.com/MrMelon54/violet/proxy"
"github.com/MrMelon54/violet/utils"
"github.com/rs/cors"
"log"
"net/http"
2023-04-21 03:21:46 +01:00
"net/url"
"path"
"strings"
)
// serveApiCors outputs the cors headers to make APIs work.
2023-04-21 03:21:46 +01:00
var serveApiCors = cors.New(cors.Options{
2023-05-29 00:01:57 +01:00
AllowedOrigins: []string{"*"}, // allow all origins for api requests
2023-04-21 03:21:46 +01:00
AllowedHeaders: []string{"Content-Type", "Authorization"},
AllowedMethods: []string{
http.MethodGet,
http.MethodHead,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodConnect,
},
AllowCredentials: true,
})
// Route is a target used by the router to manage forwarding traffic to an
// internal server using the specified configuration.
type Route struct {
Pre bool // if the path has had a prefix removed
Host string // target host
Port int // target port
Path string // target path (possibly a prefix or absolute)
Abs bool // if the path is a prefix or absolute
Cors bool // add CORS headers
SecureMode bool // use HTTPS internally
ForwardHost bool // forward host header internally
ForwardAddr bool // forward remote address
IgnoreCert bool // ignore self-cert
Headers http.Header // extra headers
Proxy http.Handler // reverse proxy handler
2023-04-21 03:21:46 +01:00
}
// IsIgnoreCert returns true if IgnoreCert is enabled.
2023-04-21 03:21:46 +01:00
func (r Route) IsIgnoreCert() bool { return r.IgnoreCert }
// UpdateHeaders takes an existing set of headers and overwrites them with the
// extra headers.
2023-04-21 03:21:46 +01:00
func (r Route) UpdateHeaders(header http.Header) {
for k, v := range r.Headers {
header[k] = v
}
}
// FullHost outputs a host:port combo or just the host if the port is 0.
2023-04-21 03:21:46 +01:00
func (r Route) FullHost() string {
if r.Port == 0 {
return r.Host
}
return fmt.Sprintf("%s:%d", r.Host, r.Port)
}
// ServeHTTP responds with the data proxied from the internal server to the
// response writer provided.
func (r Route) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
2023-04-21 03:21:46 +01:00
if r.Cors {
// wraps with CORS handler
2023-04-21 03:21:46 +01:00
serveApiCors.Handler(http.HandlerFunc(r.internalServeHTTP)).ServeHTTP(rw, req)
} else {
r.internalServeHTTP(rw, req)
}
}
// internalServeHTTP is an internal method which handles configuring the request
// for the reverse proxy handler.
2023-04-21 03:21:46 +01:00
func (r Route) internalServeHTTP(rw http.ResponseWriter, req *http.Request) {
// set the scheme and port using defaults if the port is 0
2023-04-21 03:21:46 +01:00
scheme := "http"
if r.SecureMode {
scheme = "https"
if r.Port == 0 {
r.Port = 443
}
} else {
if r.Port == 0 {
r.Port = 80
}
}
// if not Abs then join with the ending of the current path
2023-04-21 03:21:46 +01:00
p := r.Path
if !r.Abs {
p = path.Join(r.Path, req.URL.Path)
// replace the trailing slash that path.Join() strips off
if strings.HasSuffix(req.URL.Path, "/") {
p += "/"
}
2023-04-21 03:21:46 +01:00
}
// fix empty path
2023-04-21 03:21:46 +01:00
if p == "" {
p = "/"
}
// create a new URL
2023-04-21 03:21:46 +01:00
u := &url.URL{
Scheme: scheme,
Host: r.FullHost(),
Path: p,
RawQuery: req.URL.RawQuery,
}
2023-05-29 00:01:57 +01:00
// close the incoming body after use
defer req.Body.Close()
// create the internal request
2023-05-29 00:01:57 +01:00
req2, err := http.NewRequest(req.Method, u.String(), req.Body)
2023-04-21 03:21:46 +01:00
if err != nil {
log.Printf("[ServeRoute::ServeHTTP()] Error generating new request: %s\n", err)
utils.RespondHttpStatus(rw, http.StatusBadGateway)
return
}
// loops over the incoming request headers
2023-04-21 03:21:46 +01:00
for k, v := range req.Header {
// ignore host header
2023-04-21 03:21:46 +01:00
if k == "Host" {
continue
}
// copy header into the internal request
2023-04-21 03:21:46 +01:00
req2.Header[k] = v
}
// if extra route headers are set
if r.Headers != nil {
// loop over headers
for k, v := range r.Headers {
// copy header into the internal request
req2.Header[k] = v
}
}
// if forward host is enabled then send the host
2023-04-21 03:21:46 +01:00
if r.ForwardHost {
req2.Host = req.Host
}
if r.ForwardAddr {
req2.Header.Add("X-Forwarded-For", req.RemoteAddr)
}
// serve request with reverse proxy
2023-04-21 03:21:46 +01:00
r.Proxy.ServeHTTP(rw, proxy.SetReverseProxyHost(req2, r))
}
// String outputs a debug string for the route.
func (r Route) String() string {
return fmt.Sprintf("%#v", r)
}