package goxy import ( "crypto/tls" "errors" "fmt" "net/http" "net/url" ) // Routes defines a set of routes including correspondent TLS certificates type Routes map[string]Route type Route struct { ServerName *url.URL Upstream *url.URL Certificate *tls.Certificate } func (r Route) String() string { return fmt.Sprintf("%v → %v", r.ServerName, r.Upstream) } // GetCertificate returns certificate for SNI negotiation func (r Routes) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { host := h.ServerName if v, ok := r[host]; ok && v.Certificate != nil { return v.Certificate, nil } // HACK search for certs with port speciefied for k, v := range r { if k[:len(host)] == host { return v.Certificate, nil } } return nil, errors.New("no cert for " + host) } func (r Routes) ServeHTTP(w http.ResponseWriter, _ *http.Request) { for _, v := range r { fmt.Fprintln(w, v) } } func NewRoute(e Entry) (Route, error) { fail := func(err error) (Route, error) { return Route{}, err } host, err := url.Parse(e.Host) if err != nil { return fail(err) } up, err := url.Parse(e.Upstream) if err != nil { return fail(err) } if host.Host == "" || up.Host == "" { return fail(ErrNoHost) } if host.Path == "" { host.Path = "/" } r := Route{ServerName: host, Upstream: up} if host.Scheme == "https" { if e.Cert == nil || e.Key == nil { return fail(ErrNoCert) } cert, err := tls.X509KeyPair(e.Cert, e.Key) if err != nil { return fail(err) } r.Certificate = &cert } return r, nil }