You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
3.3 KiB

  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. // as opaque data.
  16. //
  17. // The key argument of the methods refers to a domain name but need not be an FQDN.
  18. // Cache implementations should not rely on the key naming pattern.
  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. select {
  71. case <-ctx.Done():
  72. // Don't overwrite the file if the context was canceled.
  73. default:
  74. newName := filepath.Join(string(d), 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), 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) (string, error) {
  108. // TempFile uses 0600 permissions
  109. f, err := ioutil.TempFile(string(d), prefix)
  110. if err != nil {
  111. return "", err
  112. }
  113. if _, err := f.Write(b); err != nil {
  114. f.Close()
  115. return "", err
  116. }
  117. return f.Name(), f.Close()
  118. }