@@ -2,24 +2,67 @@ package vhttp
22
33import (
44 "crypto/tls"
5+ "errors"
56 "fmt"
67 "net/http"
78 "strings"
9+ "sync"
810 "sync/atomic"
911
1012 "get.pme.sh/pmesh/config"
1113 "get.pme.sh/pmesh/hosts"
1214 "get.pme.sh/pmesh/netx"
1315 "get.pme.sh/pmesh/security"
1416 "get.pme.sh/pmesh/xlog"
17+ "golang.org/x/crypto/acme/autocert"
1518
1619 "github.com/samber/lo"
1720)
1821
22+ type CertProvider struct {
23+ CertPath string `yaml:"cert,omitempty"` // Path to the TLS certificate.
24+ KeyPath string `yaml:"key,omitempty"` // Path to the TLS key.
25+ certMu sync.Mutex
26+ cache atomic.Pointer [tls.Certificate ]
27+ }
28+
29+ var getLetsEncrypt = sync .OnceValue (func () * autocert.Manager {
30+ return & autocert.Manager {
31+ Prompt : autocert .AcceptTOS ,
32+ Cache : autocert .DirCache (config .CertDir .Path ()),
33+ }
34+ })
35+
36+ func (cp * CertProvider ) GetCertificate (chi * tls.ClientHelloInfo ) (cert * tls.Certificate , err error ) {
37+ if cp .CertPath == "letsencrypt" {
38+ return getLetsEncrypt ().GetCertificate (chi )
39+ } else if cp .CertPath != "" {
40+ if cert = cp .cache .Load (); cert != nil {
41+ return
42+ }
43+
44+ cp .certMu .Lock ()
45+ defer cp .certMu .Unlock ()
46+ if cert = cp .cache .Load (); cert != nil {
47+ return
48+ }
49+
50+ scert , err := security .LoadCertificate (cp .CertPath , cp .KeyPath )
51+ if err == nil {
52+ cp .cache .Store (scert .TLS )
53+ cert = scert .TLS
54+ }
55+ return cert , err
56+ }
57+ return nil , errors .New ("no certificate" )
58+ }
59+
1960type VirtualHostOptions struct {
20- Hostnames []string `yaml:"-"`
21- NoUpgrade bool `yaml:"no_upgrade,omitempty"` // Do not upgrade HTTP to HTTPS.
61+ Hostnames []string `yaml:"-"`
62+ NoUpgrade bool `yaml:"no_upgrade,omitempty"` // Do not upgrade HTTP to HTTPS.
63+ Certs map [string ]* CertProvider `yaml:"certs,omitempty"` // TLS certificates.
2264}
65+
2366type VirtualHost struct {
2467 VirtualHostOptions
2568 Mux
@@ -140,6 +183,14 @@ func (mux *TopLevelMux) GetCertificate(chi *tls.ClientHelloInfo) (cert *tls.Cert
140183 if sni := chi .ServerName ; sni != "" {
141184 _ , _ , vhg = hosts .MatchMap (unique , sni )
142185 if vhg != nil {
186+ for _ , vh := range vhg .hosts {
187+ if vh .Certs != nil {
188+ _ , _ , prov := hosts .MatchMap (vh .Certs , sni )
189+ if prov != nil {
190+ return prov .GetCertificate (chi )
191+ }
192+ }
193+ }
143194 cert = security .ObtainCertificate (config .Get ().Secret , sni ).TLS
144195 }
145196 }
0 commit comments