cache.go 3.5 KB

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