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 5package autocert 6 7import ( 8 "context" 9 "errors" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13) 14 15// ErrCacheMiss is returned when a certificate is not found in cache. 16var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss") 17 18// Cache is used by Manager to store and retrieve previously obtained certificates 19// as opaque data. 20// 21// The key argument of the methods refers to a domain name but need not be an FQDN. 22// Cache implementations should not rely on the key naming pattern. 23type Cache interface { 24 // Get returns a certificate data for the specified key. 25 // If there's no such key, Get returns ErrCacheMiss. 26 Get(ctx context.Context, key string) ([]byte, error) 27 28 // Put stores the data in the cache under the specified key. 29 // Underlying implementations may use any data storage format, 30 // as long as the reverse operation, Get, results in the original data. 31 Put(ctx context.Context, key string, data []byte) error 32 33 // Delete removes a certificate data from the cache under the specified key. 34 // If there's no such key in the cache, Delete returns nil. 35 Delete(ctx context.Context, key string) error 36} 37 38// DirCache implements Cache using a directory on the local filesystem. 39// If the directory does not exist, it will be created with 0700 permissions. 40type DirCache string 41 42// Get reads a certificate data from the specified file name. 43func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) { 44 name = filepath.Join(string(d), name) 45 var ( 46 data []byte 47 err error 48 done = make(chan struct{}) 49 ) 50 go func() { 51 data, err = ioutil.ReadFile(name) 52 close(done) 53 }() 54 select { 55 case <-ctx.Done(): 56 return nil, ctx.Err() 57 case <-done: 58 } 59 if os.IsNotExist(err) { 60 return nil, ErrCacheMiss 61 } 62 return data, err 63} 64 65// Put writes the certificate data to the specified file name. 66// The file will be created with 0600 permissions. 67func (d DirCache) Put(ctx context.Context, name string, data []byte) error { 68 if err := os.MkdirAll(string(d), 0700); err != nil { 69 return err 70 } 71 72 done := make(chan struct{}) 73 var err error 74 go func() { 75 defer close(done) 76 var tmp string 77 if tmp, err = d.writeTempFile(name, data); err != nil { 78 return 79 } 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), 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. 97func (d DirCache) Delete(ctx context.Context, name string) error { 98 name = filepath.Join(string(d), 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. 119func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) { 120 // TempFile uses 0600 permissions 121 f, err := ioutil.TempFile(string(d), prefix) 122 if err != nil { 123 return "", err 124 } 125 if _, err := f.Write(b); err != nil { 126 f.Close() 127 return "", err 128 } 129 return f.Name(), f.Close() 130} 131