acme: improve http-01 challenge API

This makes http-01 handling more consistent with tls-sni-* methods
and, hopefully, future implementation such as dns-01 and oob-01.

Change-Id: If1a4a47ee8528ef19960fee4e6860392c1813fe6
Reviewed-on: https://go-review.googlesource.com/27011
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/acme/internal/acme/acme.go b/acme/internal/acme/acme.go
index ac9c5e0..a5554ff 100644
--- a/acme/internal/acme/acme.go
+++ b/acme/internal/acme/acme.go
@@ -377,22 +377,23 @@
 	return v.challenge(), nil
 }
 
-// HTTP01Handler creates a new handler which responds to a http-01 challenge.
+// HTTP01ChallengeResponse returns the response for an http-01 challenge.
+// Servers should respond with the value to HTTP requests at the URL path
+// provided by HTTP01ChallengePath to validate the challenge and prove control
+// over a domain name.
+//
 // The token argument is a Challenge.Token value.
-func (c *Client) HTTP01Handler(token string) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		if !strings.HasSuffix(r.URL.Path, token) {
-			w.WriteHeader(http.StatusNotFound)
-			return
-		}
-		w.Header().Set("content-type", "text/plain")
-		auth, err := keyAuth(c.Key.Public(), token)
-		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
-		}
-		w.Write([]byte(auth))
-	})
+func (c *Client) HTTP01ChallengeResponse(token string) (string, error) {
+	return keyAuth(c.Key.Public(), token)
+}
+
+// HTTP01ChallengePath returns the URL path at which the response for an http-01 challenge
+// should be provided by the servers.
+// The response value can be obtained with HTTP01ChallengeResponse.
+//
+// The token argument is a Challenge.Token value.
+func (c *Client) HTTP01ChallengePath(token string) string {
+	return "/.well-known/acme-challenge/" + token
 }
 
 // TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
diff --git a/acme/internal/acme/acme_test.go b/acme/internal/acme/acme_test.go
index 37378c5..56a69ae 100644
--- a/acme/internal/acme/acme_test.go
+++ b/acme/internal/acme/acme_test.go
@@ -892,3 +892,23 @@
 		t.Errorf("%v doesn't have %q", cert.DNSNames, name)
 	}
 }
+
+func TestHTTP01Challenge(t *testing.T) {
+	const (
+		token = "xxx"
+		// thumbprint is precomputed for testKey in jws_test.go
+		value   = token + "." + testKeyThumbprint
+		urlpath = "/.well-known/acme-challenge/" + token
+	)
+	client := &Client{Key: testKey}
+	val, err := client.HTTP01ChallengeResponse(token)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if val != value {
+		t.Errorf("val = %q; want %q", val, value)
+	}
+	if path := client.HTTP01ChallengePath(token); path != urlpath {
+		t.Errorf("path = %q; want %q", path, urlpath)
+	}
+}