cache.go (3551B)
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 "errors" 10 "os" 11 "path/filepath" 12 ) 13 14 // ErrCacheMiss is returned when a certificate is not found in cache. 15 var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss") 16 17 // Cache is used by Manager to store and retrieve previously obtained certificates 18 // and other account data as opaque blobs. 19 // 20 // Cache implementations should not rely on the key naming pattern. Keys can 21 // include any printable ASCII characters, except the following: \/:*?"<>| 22 type Cache interface { 23 // Get returns a certificate data for the specified key. 24 // If there's no such key, Get returns ErrCacheMiss. 25 Get(ctx context.Context, key string) ([]byte, error) 26 27 // Put stores the data in the cache under the specified key. 28 // Underlying implementations may use any data storage format, 29 // as long as the reverse operation, Get, results in the original data. 30 Put(ctx context.Context, key string, data []byte) error 31 32 // Delete removes a certificate data from the cache under the specified key. 33 // If there's no such key in the cache, Delete returns nil. 34 Delete(ctx context.Context, key string) error 35 } 36 37 // DirCache implements Cache using a directory on the local filesystem. 38 // If the directory does not exist, it will be created with 0700 permissions. 39 type DirCache string 40 41 // Get reads a certificate data from the specified file name. 42 func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) { 43 name = filepath.Join(string(d), filepath.Clean("/"+name)) 44 var ( 45 data []byte 46 err error 47 done = make(chan struct{}) 48 ) 49 go func() { 50 data, err = os.ReadFile(name) 51 close(done) 52 }() 53 select { 54 case <-ctx.Done(): 55 return nil, ctx.Err() 56 case <-done: 57 } 58 if os.IsNotExist(err) { 59 return nil, ErrCacheMiss 60 } 61 return data, err 62 } 63 64 // Put writes the certificate data to the specified file name. 65 // The file will be created with 0600 permissions. 66 func (d DirCache) Put(ctx context.Context, name string, data []byte) error { 67 if err := os.MkdirAll(string(d), 0700); err != nil { 68 return err 69 } 70 71 done := make(chan struct{}) 72 var err error 73 go func() { 74 defer close(done) 75 var tmp string 76 if tmp, err = d.writeTempFile(name, data); err != nil { 77 return 78 } 79 defer os.Remove(tmp) 80 select { 81 case <-ctx.Done(): 82 // Don't overwrite the file if the context was canceled. 83 default: 84 newName := filepath.Join(string(d), filepath.Clean("/"+name)) 85 err = os.Rename(tmp, newName) 86 } 87 }() 88 select { 89 case <-ctx.Done(): 90 return ctx.Err() 91 case <-done: 92 } 93 return err 94 } 95 96 // Delete removes the specified file name. 97 func (d DirCache) Delete(ctx context.Context, name string) error { 98 name = filepath.Join(string(d), filepath.Clean("/"+name)) 99 var ( 100 err error 101 done = make(chan struct{}) 102 ) 103 go func() { 104 err = os.Remove(name) 105 close(done) 106 }() 107 select { 108 case <-ctx.Done(): 109 return ctx.Err() 110 case <-done: 111 } 112 if err != nil && !os.IsNotExist(err) { 113 return err 114 } 115 return nil 116 } 117 118 // writeTempFile writes b to a temporary file, closes the file and returns its path. 119 func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) { 120 // TempFile uses 0600 permissions 121 f, err := os.CreateTemp(string(d), prefix) 122 if err != nil { 123 return "", err 124 } 125 defer func() { 126 if reterr != nil { 127 os.Remove(f.Name()) 128 } 129 }() 130 if _, err := f.Write(b); err != nil { 131 f.Close() 132 return "", err 133 } 134 return f.Name(), f.Close() 135 }