1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
21 "golang.org/x/crypto/acme"
24 func TestRenewalNext(t *testing.T) {
26 timeNow = func() time.Time { return now }
27 defer func() { timeNow = time.Now }()
29 man := &Manager{RenewBefore: 7 * 24 * time.Hour}
33 min, max time.Duration
35 {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour},
36 {now.Add(time.Hour), 0, 1},
38 {now.Add(-time.Hour), 0, 1},
41 dr := &domainRenewal{m: man}
42 for i, test := range tt {
43 next := dr.next(test.expiry)
44 if next < test.min || test.max < next {
45 t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max)
50 func TestRenewFromCache(t *testing.T) {
51 const domain = "example.org"
53 // ACME CA server stub
54 var ca *httptest.Server
55 ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
56 w.Header().Set("Replay-Nonce", "nonce")
57 if r.Method == "HEAD" {
65 if err := discoTmpl.Execute(w, ca.URL); err != nil {
66 t.Fatalf("discoTmpl: %v", err)
68 // client key registration
71 // domain authorization
73 w.Header().Set("Location", ca.URL+"/authz/1")
74 w.WriteHeader(http.StatusCreated)
75 w.Write([]byte(`{"status": "valid"}`))
79 CSR string `json:"csr"`
81 decodePayload(&req, r.Body)
82 b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
83 csr, err := x509.ParseCertificateRequest(b)
85 t.Fatalf("new-cert: CSR: %v", err)
87 der, err := dummyCert(csr.PublicKey, domain)
89 t.Fatalf("new-cert: dummyCert: %v", err)
91 chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
92 w.Header().Set("Link", chainUp)
93 w.WriteHeader(http.StatusCreated)
97 der, err := dummyCert(nil, "ca")
99 t.Fatalf("ca-cert: dummyCert: %v", err)
103 t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
108 // use EC key to run faster on 386
109 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
115 Cache: newMemCache(),
116 RenewBefore: 24 * time.Hour,
117 Client: &acme.Client{
119 DirectoryURL: ca.URL,
122 defer man.stopRenew()
124 // cache an almost expired cert
126 cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), domain)
130 tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
131 if err := man.cachePut(context.Background(), domain, tlscert); err != nil {
135 // veriy the renewal happened
137 testDidRenewLoop = func(next time.Duration, err error) {}
139 done := make(chan struct{})
140 testDidRenewLoop = func(next time.Duration, err error) {
143 t.Errorf("testDidRenewLoop: %v", err)
145 // Next should be about 90 days:
146 // dummyCert creates 90days expiry + account for man.RenewBefore.
147 // Previous expiration was within 1 min.
148 future := 88 * 24 * time.Hour
150 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
153 // ensure the new cert is cached
154 after := time.Now().Add(future)
155 tlscert, err := man.cacheGet(context.Background(), domain)
157 t.Fatalf("man.cacheGet: %v", err)
159 if !tlscert.Leaf.NotAfter.After(after) {
160 t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
163 // verify the old cert is also replaced in memory
165 defer man.stateMu.Unlock()
166 s := man.state[domain]
168 t.Fatalf("m.state[%q] is nil", domain)
170 tlscert, err = s.tlscert()
172 t.Fatalf("s.tlscert: %v", err)
174 if !tlscert.Leaf.NotAfter.After(after) {
175 t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
180 hello := &tls.ClientHelloInfo{ServerName: domain}
181 if _, err := man.GetCertificate(hello); err != nil {
185 // wait for renew loop
187 case <-time.After(10 * time.Second):
188 t.Fatal("renew took too long to occur")