package main import ( "crypto/tls" "errors" "net/http" "net/http/httputil" "net/url" "strings" ) // Route defines a set of routes including correspondent TLS certificates type Route map[string]Entry // Entry holds routing settings type Entry struct { ServerName string Upstream string Cert []byte Key []byte cert *tls.Certificate } // GetCertificate returns certificate for SNI negotiation func (r Route) GetCertificate(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 NewReverseProxy(target *url.URL) *httputil.ReverseProxy { director := func(req *http.Request) { //log.Println("director", req) req.URL.Scheme = target.Scheme req.URL.Host = target.Host } return &httputil.ReverseProxy{Director: director} } // Restore and update routes from in-memory state 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 } if !strings.HasSuffix(v.ServerName, "/") { v.ServerName += "/" } //mux.Handle(v.ServerName, httputil.NewSingleHostReverseProxy(up)) switch up.Scheme { case "ws": mux.Handle(v.ServerName, NewWebSocketProxy(up)) default: mux.Handle(v.ServerName, NewReverseProxy(up)) } } server.Handler = mux return nil } func (e Entry) String() string { ret := e.ServerName + " → " + e.Upstream if e.cert != nil { ret += " with TLS" } return ret }