cache.go 3.4 KB

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