OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / crypto / acme / autocert / renewal.go
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.
4
5 package autocert
6
7 import (
8         "context"
9         "crypto"
10         "sync"
11         "time"
12 )
13
14 // renewJitter is the maximum deviation from Manager.RenewBefore.
15 const renewJitter = time.Hour
16
17 // domainRenewal tracks the state used by the periodic timers
18 // renewing a single domain's cert.
19 type domainRenewal struct {
20         m      *Manager
21         domain string
22         key    crypto.Signer
23
24         timerMu sync.Mutex
25         timer   *time.Timer
26 }
27
28 // start starts a cert renewal timer at the time
29 // defined by the certificate expiration time exp.
30 //
31 // If the timer is already started, calling start is a noop.
32 func (dr *domainRenewal) start(exp time.Time) {
33         dr.timerMu.Lock()
34         defer dr.timerMu.Unlock()
35         if dr.timer != nil {
36                 return
37         }
38         dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
39 }
40
41 // stop stops the cert renewal timer.
42 // If the timer is already stopped, calling stop is a noop.
43 func (dr *domainRenewal) stop() {
44         dr.timerMu.Lock()
45         defer dr.timerMu.Unlock()
46         if dr.timer == nil {
47                 return
48         }
49         dr.timer.Stop()
50         dr.timer = nil
51 }
52
53 // renew is called periodically by a timer.
54 // The first renew call is kicked off by dr.start.
55 func (dr *domainRenewal) renew() {
56         dr.timerMu.Lock()
57         defer dr.timerMu.Unlock()
58         if dr.timer == nil {
59                 return
60         }
61
62         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
63         defer cancel()
64         // TODO: rotate dr.key at some point?
65         next, err := dr.do(ctx)
66         if err != nil {
67                 next = renewJitter / 2
68                 next += time.Duration(pseudoRand.int63n(int64(next)))
69         }
70         dr.timer = time.AfterFunc(next, dr.renew)
71         testDidRenewLoop(next, err)
72 }
73
74 // do is similar to Manager.createCert but it doesn't lock a Manager.state item.
75 // Instead, it requests a new certificate independently and, upon success,
76 // replaces dr.m.state item with a new one and updates cache for the given domain.
77 //
78 // It may return immediately if the expiration date of the currently cached cert
79 // is far enough in the future.
80 //
81 // The returned value is a time interval after which the renewal should occur again.
82 func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
83         // a race is likely unavoidable in a distributed environment
84         // but we try nonetheless
85         if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil {
86                 next := dr.next(tlscert.Leaf.NotAfter)
87                 if next > dr.m.renewBefore()+renewJitter {
88                         return next, nil
89                 }
90         }
91
92         der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.domain)
93         if err != nil {
94                 return 0, err
95         }
96         state := &certState{
97                 key:  dr.key,
98                 cert: der,
99                 leaf: leaf,
100         }
101         tlscert, err := state.tlscert()
102         if err != nil {
103                 return 0, err
104         }
105         dr.m.cachePut(ctx, dr.domain, tlscert)
106         dr.m.stateMu.Lock()
107         defer dr.m.stateMu.Unlock()
108         // m.state is guaranteed to be non-nil at this point
109         dr.m.state[dr.domain] = state
110         return dr.next(leaf.NotAfter), nil
111 }
112
113 func (dr *domainRenewal) next(expiry time.Time) time.Duration {
114         d := expiry.Sub(timeNow()) - dr.m.renewBefore()
115         // add a bit of randomness to renew deadline
116         n := pseudoRand.int63n(int64(renewJitter))
117         d -= time.Duration(n)
118         if d < 0 {
119                 return 0
120         }
121         return d
122 }
123
124 var testDidRenewLoop = func(next time.Duration, err error) {}