gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

renewal.go (4263B)


      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 	ck  certKey
     22 	key crypto.Signer
     23 
     24 	timerMu    sync.Mutex
     25 	timer      *time.Timer
     26 	timerClose chan struct{} // if non-nil, renew closes this channel (and nils out the timer fields) instead of running
     27 }
     28 
     29 // start starts a cert renewal timer at the time
     30 // defined by the certificate expiration time exp.
     31 //
     32 // If the timer is already started, calling start is a noop.
     33 func (dr *domainRenewal) start(exp time.Time) {
     34 	dr.timerMu.Lock()
     35 	defer dr.timerMu.Unlock()
     36 	if dr.timer != nil {
     37 		return
     38 	}
     39 	dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
     40 }
     41 
     42 // stop stops the cert renewal timer and waits for any in-flight calls to renew
     43 // to complete. If the timer is already stopped, calling stop is a noop.
     44 func (dr *domainRenewal) stop() {
     45 	dr.timerMu.Lock()
     46 	defer dr.timerMu.Unlock()
     47 	for {
     48 		if dr.timer == nil {
     49 			return
     50 		}
     51 		if dr.timer.Stop() {
     52 			dr.timer = nil
     53 			return
     54 		} else {
     55 			// dr.timer fired, and we acquired dr.timerMu before the renew callback did.
     56 			// (We know this because otherwise the renew callback would have reset dr.timer!)
     57 			timerClose := make(chan struct{})
     58 			dr.timerClose = timerClose
     59 			dr.timerMu.Unlock()
     60 			<-timerClose
     61 			dr.timerMu.Lock()
     62 		}
     63 	}
     64 }
     65 
     66 // renew is called periodically by a timer.
     67 // The first renew call is kicked off by dr.start.
     68 func (dr *domainRenewal) renew() {
     69 	dr.timerMu.Lock()
     70 	defer dr.timerMu.Unlock()
     71 	if dr.timerClose != nil {
     72 		close(dr.timerClose)
     73 		dr.timer, dr.timerClose = nil, nil
     74 		return
     75 	}
     76 
     77 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
     78 	defer cancel()
     79 	// TODO: rotate dr.key at some point?
     80 	next, err := dr.do(ctx)
     81 	if err != nil {
     82 		next = renewJitter / 2
     83 		next += time.Duration(pseudoRand.int63n(int64(next)))
     84 	}
     85 	testDidRenewLoop(next, err)
     86 	dr.timer = time.AfterFunc(next, dr.renew)
     87 }
     88 
     89 // updateState locks and replaces the relevant Manager.state item with the given
     90 // state. It additionally updates dr.key with the given state's key.
     91 func (dr *domainRenewal) updateState(state *certState) {
     92 	dr.m.stateMu.Lock()
     93 	defer dr.m.stateMu.Unlock()
     94 	dr.key = state.key
     95 	dr.m.state[dr.ck] = state
     96 }
     97 
     98 // do is similar to Manager.createCert but it doesn't lock a Manager.state item.
     99 // Instead, it requests a new certificate independently and, upon success,
    100 // replaces dr.m.state item with a new one and updates cache for the given domain.
    101 //
    102 // It may lock and update the Manager.state if the expiration date of the currently
    103 // cached cert is far enough in the future.
    104 //
    105 // The returned value is a time interval after which the renewal should occur again.
    106 func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
    107 	// a race is likely unavoidable in a distributed environment
    108 	// but we try nonetheless
    109 	if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
    110 		next := dr.next(tlscert.Leaf.NotAfter)
    111 		if next > dr.m.renewBefore()+renewJitter {
    112 			signer, ok := tlscert.PrivateKey.(crypto.Signer)
    113 			if ok {
    114 				state := &certState{
    115 					key:  signer,
    116 					cert: tlscert.Certificate,
    117 					leaf: tlscert.Leaf,
    118 				}
    119 				dr.updateState(state)
    120 				return next, nil
    121 			}
    122 		}
    123 	}
    124 
    125 	der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.ck)
    126 	if err != nil {
    127 		return 0, err
    128 	}
    129 	state := &certState{
    130 		key:  dr.key,
    131 		cert: der,
    132 		leaf: leaf,
    133 	}
    134 	tlscert, err := state.tlscert()
    135 	if err != nil {
    136 		return 0, err
    137 	}
    138 	if err := dr.m.cachePut(ctx, dr.ck, tlscert); err != nil {
    139 		return 0, err
    140 	}
    141 	dr.updateState(state)
    142 	return dr.next(leaf.NotAfter), nil
    143 }
    144 
    145 func (dr *domainRenewal) next(expiry time.Time) time.Duration {
    146 	d := expiry.Sub(dr.m.now()) - dr.m.renewBefore()
    147 	// add a bit of randomness to renew deadline
    148 	n := pseudoRand.int63n(int64(renewJitter))
    149 	d -= time.Duration(n)
    150 	if d < 0 {
    151 		return 0
    152 	}
    153 	return d
    154 }
    155 
    156 var testDidRenewLoop = func(next time.Duration, err error) {}