package acmeserver import ( "context" "net/http" "sync" "golang.org/x/crypto/acme" ) // httpValidator can validate http-01 challenges. It serves // validation tokens under the /.well-known/acme-challenge path when // used as an HTTP handler. type httpValidator struct { mx sync.Mutex tokens map[string][]byte } func newHTTPValidator() *httpValidator { return &httpValidator{ tokens: make(map[string][]byte), } } func (h *httpValidator) Fulfill(_ context.Context, client *acme.Client, _ string, chal *acme.Challenge) (func(), error) { resp, err := client.HTTP01ChallengeResponse(chal.Token) if err != nil { return nil, err } path := client.HTTP01ChallengePath(chal.Token) h.mx.Lock() h.tokens[path] = []byte(resp) h.mx.Unlock() return func() { go func() { h.mx.Lock() delete(h.tokens, path) h.mx.Unlock() }() }, nil } func (h *httpValidator) ServeHTTP(w http.ResponseWriter, r *http.Request) { // No defer because it's not polite to Write() while holding the lock. h.mx.Lock() data, ok := h.tokens[r.URL.Path] h.mx.Unlock() if !ok { http.NotFound(w, r) return } // Not interested in errors writing the response. w.Write(data) // nolint }