package main import ( "crypto/tls" "errors" "flag" "log" "net/http" "net/http/httputil" "net/rpc" "net/url" ) var ( config = flag.String("conf", "certs/goxy.yml", "configuration file") listen = flag.String("listen", ":http", "HTTP port") listenTLS = flag.String("listentls", ":https", "TLS port") listenRPC = flag.String("listenrpc", ":http-alt", "RPC port") data = flag.String("data", "data/goxy.gob", "persistent storage file") route = make(Route) mux = http.NewServeMux() ) type Route map[string]Entry type Entry struct { ServerName string Upstream string 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() { // FIXME assignment copies lock value to *mux: net/http.ServeMux contains sync.RWMutex *mux = *http.NewServeMux() for _, e := range route { e.NewHandle() } } func (e Entry) NewHandle() error { log.Println("New handle", e) up, err := url.Parse(e.Upstream) if err != nil { return err } mux.Handle(e.ServerName+"/", httputil.NewSingleHostReverseProxy(up)) 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 StartHTTP(listen string) { log.Println("listen", listen, "(HTTP)") s := http.Server{ Addr: listen, Handler: mux, } log.Fatal(s.ListenAndServe()) } func StartTLS(listen string) { log.Println("listen", listen, "(TLS)") s := http.Server{ Addr: listen, Handler: mux, TLSConfig: &tls.Config{GetCertificate: route.SNI}, } log.Fatal(s.ListenAndServeTLS("", "")) } func StartRPC(listen string) { log.Println("listen", listen, "(RPC)") rpc.HandleHTTP() log.Fatal(http.ListenAndServe(listen, nil)) } func main() { flag.Parse() if err := route.Load(*data); err != nil { log.Println(err) } route.Restore() go StartHTTP(*listen) go StartTLS(*listenTLS) go StartRPC(*listenRPC) select {} }