package main import ( "crypto/tls" "errors" "flag" "log" "net/http" "net/http/httputil" "net/url" _ "net/http/pprof" ) var ( data = flag.String("data", "data/goxy.gob", "persistent storage file") route = make(Route) server = http.Server{ Handler: http.NewServeMux(), TLSConfig: &tls.Config{GetCertificate: route.SNI}, } ) type Route map[string]Entry type Entry struct { ServerName string Upstream string Cert []byte Key []byte cert *tls.Certificate } func (r Route) SNI(h *tls.ClientHelloInfo) (*tls.Certificate, error) { if e, ok := r[h.ServerName]; ok && e.cert != nil { return e.cert, nil } return nil, errors.New("no cert for " + h.ServerName) } func (r Route) Restore() error { mux := http.NewServeMux() for k, v := range route { if v.Cert != nil && v.Key != nil { cert, err := tls.X509KeyPair(v.Cert, v.Key) if err != nil { return err } v.cert = &cert r[k] = v } up, err := url.Parse(v.Upstream) if err != nil { return err } mux.Handle(v.ServerName+"/", httputil.NewSingleHostReverseProxy(up)) } server.Handler = mux return nil } func (e Entry) String() string { if e.cert != nil { return e.ServerName + " -> " + e.Upstream + " with TLS" } else { return e.ServerName + " -> " + e.Upstream } } func main() { flag.Parse() if err := route.Load(*data); err != nil { log.Println(err) } if err := route.Restore(); err != nil { log.Fatal(err) } errc := make(chan error) go func() { errc <- server.ListenAndServe() }() go func() { errc <- server.ListenAndServeTLS("", "") }() go func() { errc <- http.ListenAndServe(":http-alt", nil) }() log.Fatal(<-errc) }