141 lines
3.0 KiB
Go
141 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
func main() {
|
|
c, err := NewConfig()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
s := &http.Server{
|
|
Addr: fmt.Sprintf(":%d", c.Port),
|
|
Handler: c,
|
|
}
|
|
foo := s.ListenAndServe
|
|
if c.Cert.CRT != "" {
|
|
foo = func() error {
|
|
return s.ListenAndServeTLS(c.Cert.CRT, c.Cert.Key)
|
|
}
|
|
}
|
|
log.Printf("listening on %v...", s.Addr)
|
|
if err := foo(); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (c Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
endpoint := c.endpoint(r)
|
|
if endpoint.To == "" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
if r.Method == http.MethodOptions {
|
|
cors(w)
|
|
return
|
|
}
|
|
|
|
if r.URL.Scheme == "https" {
|
|
w.Header().Set("X-Forwarded-Proto", "https")
|
|
}
|
|
|
|
if c.handleAdmin(w, r) {
|
|
return
|
|
}
|
|
|
|
if !c.basicAuth(w, r) {
|
|
return
|
|
}
|
|
|
|
u, err := url.Parse(endpoint.To)
|
|
if err != nil {
|
|
log.Printf("[%s] %v", c.key(r), err)
|
|
}
|
|
var transport http.Transport
|
|
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
proxy := httputil.NewSingleHostReverseProxy(u)
|
|
proxy.Transport = redirPurge{
|
|
proxyHost: r.Host,
|
|
targetHost: u.Host,
|
|
baseTransport: &transport,
|
|
}
|
|
proxy.ServeHTTP(w, r)
|
|
}
|
|
|
|
func cors(w http.ResponseWriter) {
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Access-Control-Allow-Headers", "X-Auth-Token, content-type, Content-Type")
|
|
w.Header().Set("Content-Length", "0")
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, TRACE, PATCH, HEAD, DELETE")
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
func (c Config) key(r *http.Request) string {
|
|
return strings.Split(r.Host, ".")[0]
|
|
}
|
|
|
|
func (c Config) handleAdmin(w http.ResponseWriter, r *http.Request) bool {
|
|
switch c.key(r) {
|
|
case "_":
|
|
panic("not impl: list")
|
|
case "home":
|
|
panic("not impl: home")
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c Config) basicAuth(w http.ResponseWriter, r *http.Request) bool {
|
|
basicAuth := c.endpoint(r).BasicAuth
|
|
if noAuth := basicAuth == ""; noAuth {
|
|
return true
|
|
}
|
|
|
|
u, p, _ := r.BasicAuth()
|
|
if fmt.Sprintf("%s:%s", u, p) != basicAuth {
|
|
w.Header().Set("WWW-Authenticate", "Basic")
|
|
http.Error(w, "unexpected basic auth", http.StatusUnauthorized)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (c Config) endpoint(r *http.Request) Endpoint {
|
|
key := c.key(r)
|
|
domain := strings.TrimPrefix(r.Host, key)
|
|
m, ok := c.Domains[domain]
|
|
if !ok {
|
|
return Endpoint{}
|
|
}
|
|
return m[c.key(r)]
|
|
}
|
|
|
|
type redirPurge struct {
|
|
proxyHost string
|
|
targetHost string
|
|
baseTransport http.RoundTripper
|
|
}
|
|
|
|
func (rp redirPurge) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
resp, err := rp.baseTransport.RoundTrip(r)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
if loc := resp.Header.Get("Location"); loc != "" {
|
|
resp.Header.Set("Location", strings.Replace(loc, rp.targetHost, rp.proxyHost, 1))
|
|
}
|
|
// google floc https://paramdeo.com/blog/opting-your-website-out-of-googles-floc-network
|
|
resp.Header.Set("Permissions-Policy", "interest-cohort=()")
|
|
return resp, err
|
|
}
|