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// and other account data as opaque blobs. 20// 21// Cache implementations should not rely on the key naming pattern. Keys can 22// include any printable ASCII characters, except the following: \/:*?"<>| 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 defer os.Remove(tmp) 81 select { 82 case <-ctx.Done(): 83 // Don't overwrite the file if the context was canceled. 84 default: 85 newName := filepath.Join(string(d), name) 86 err = os.Rename(tmp, newName) 87 } 88 }() 89 select { 90 case <-ctx.Done(): 91 return ctx.Err() 92 case <-done: 93 } 94 return err 95} 96 97// Delete removes the specified file name. 98func (d DirCache) Delete(ctx context.Context, name string) error { 99 name = filepath.Join(string(d), name) 100 var ( 101 err error 102 done = make(chan struct{}) 103 ) 104 go func() { 105 err = os.Remove(name) 106 close(done) 107 }() 108 select { 109 case <-ctx.Done(): 110 return ctx.Err() 111 case <-done: 112 } 113 if err != nil && !os.IsNotExist(err) { 114 return err 115 } 116 return nil 117} 118 119// writeTempFile writes b to a temporary file, closes the file and returns its path. 120func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) { 121 // TempFile uses 0600 permissions 122 f, err := ioutil.TempFile(string(d), prefix) 123 if err != nil { 124 return "", err 125 } 126 defer func() { 127 if reterr != nil { 128 os.Remove(f.Name()) 129 } 130 }() 131 if _, err := f.Write(b); err != nil { 132 f.Close() 133 return "", err 134 } 135 return f.Name(), f.Close() 136} 137