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) { if c.endpoint(r).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 } endpoint := c.endpoint(r) 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 { return c.Endpoints[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 }