131 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			131 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2016 The Go Authors. All rights reserved.
							 | 
						||
| 
								 | 
							
								// Use of this source code is governed by a BSD-style
							 | 
						||
| 
								 | 
							
								// license that can be found in the LICENSE file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package autocert
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"context"
							 | 
						||
| 
								 | 
							
									"errors"
							 | 
						||
| 
								 | 
							
									"io/ioutil"
							 | 
						||
| 
								 | 
							
									"os"
							 | 
						||
| 
								 | 
							
									"path/filepath"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ErrCacheMiss is returned when a certificate is not found in cache.
							 | 
						||
| 
								 | 
							
								var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Cache is used by Manager to store and retrieve previously obtained certificates
							 | 
						||
| 
								 | 
							
								// and other account data as opaque blobs.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Cache implementations should not rely on the key naming pattern. Keys can
							 | 
						||
| 
								 | 
							
								// include any printable ASCII characters, except the following: \/:*?"<>|
							 | 
						||
| 
								 | 
							
								type Cache interface {
							 | 
						||
| 
								 | 
							
									// Get returns a certificate data for the specified key.
							 | 
						||
| 
								 | 
							
									// If there's no such key, Get returns ErrCacheMiss.
							 | 
						||
| 
								 | 
							
									Get(ctx context.Context, key string) ([]byte, error)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Put stores the data in the cache under the specified key.
							 | 
						||
| 
								 | 
							
									// Underlying implementations may use any data storage format,
							 | 
						||
| 
								 | 
							
									// as long as the reverse operation, Get, results in the original data.
							 | 
						||
| 
								 | 
							
									Put(ctx context.Context, key string, data []byte) error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Delete removes a certificate data from the cache under the specified key.
							 | 
						||
| 
								 | 
							
									// If there's no such key in the cache, Delete returns nil.
							 | 
						||
| 
								 | 
							
									Delete(ctx context.Context, key string) error
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// DirCache implements Cache using a directory on the local filesystem.
							 | 
						||
| 
								 | 
							
								// If the directory does not exist, it will be created with 0700 permissions.
							 | 
						||
| 
								 | 
							
								type DirCache string
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Get reads a certificate data from the specified file name.
							 | 
						||
| 
								 | 
							
								func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
							 | 
						||
| 
								 | 
							
									name = filepath.Join(string(d), name)
							 | 
						||
| 
								 | 
							
									var (
							 | 
						||
| 
								 | 
							
										data []byte
							 | 
						||
| 
								 | 
							
										err  error
							 | 
						||
| 
								 | 
							
										done = make(chan struct{})
							 | 
						||
| 
								 | 
							
									)
							 | 
						||
| 
								 | 
							
									go func() {
							 | 
						||
| 
								 | 
							
										data, err = ioutil.ReadFile(name)
							 | 
						||
| 
								 | 
							
										close(done)
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
									select {
							 | 
						||
| 
								 | 
							
									case <-ctx.Done():
							 | 
						||
| 
								 | 
							
										return nil, ctx.Err()
							 | 
						||
| 
								 | 
							
									case <-done:
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if os.IsNotExist(err) {
							 | 
						||
| 
								 | 
							
										return nil, ErrCacheMiss
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return data, err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Put writes the certificate data to the specified file name.
							 | 
						||
| 
								 | 
							
								// The file will be created with 0600 permissions.
							 | 
						||
| 
								 | 
							
								func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
							 | 
						||
| 
								 | 
							
									if err := os.MkdirAll(string(d), 0700); err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									done := make(chan struct{})
							 | 
						||
| 
								 | 
							
									var err error
							 | 
						||
| 
								 | 
							
									go func() {
							 | 
						||
| 
								 | 
							
										defer close(done)
							 | 
						||
| 
								 | 
							
										var tmp string
							 | 
						||
| 
								 | 
							
										if tmp, err = d.writeTempFile(name, data); err != nil {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										select {
							 | 
						||
| 
								 | 
							
										case <-ctx.Done():
							 | 
						||
| 
								 | 
							
											// Don't overwrite the file if the context was canceled.
							 | 
						||
| 
								 | 
							
										default:
							 | 
						||
| 
								 | 
							
											newName := filepath.Join(string(d), name)
							 | 
						||
| 
								 | 
							
											err = os.Rename(tmp, newName)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
									select {
							 | 
						||
| 
								 | 
							
									case <-ctx.Done():
							 | 
						||
| 
								 | 
							
										return ctx.Err()
							 | 
						||
| 
								 | 
							
									case <-done:
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Delete removes the specified file name.
							 | 
						||
| 
								 | 
							
								func (d DirCache) Delete(ctx context.Context, name string) error {
							 | 
						||
| 
								 | 
							
									name = filepath.Join(string(d), name)
							 | 
						||
| 
								 | 
							
									var (
							 | 
						||
| 
								 | 
							
										err  error
							 | 
						||
| 
								 | 
							
										done = make(chan struct{})
							 | 
						||
| 
								 | 
							
									)
							 | 
						||
| 
								 | 
							
									go func() {
							 | 
						||
| 
								 | 
							
										err = os.Remove(name)
							 | 
						||
| 
								 | 
							
										close(done)
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
									select {
							 | 
						||
| 
								 | 
							
									case <-ctx.Done():
							 | 
						||
| 
								 | 
							
										return ctx.Err()
							 | 
						||
| 
								 | 
							
									case <-done:
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if err != nil && !os.IsNotExist(err) {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// writeTempFile writes b to a temporary file, closes the file and returns its path.
							 | 
						||
| 
								 | 
							
								func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) {
							 | 
						||
| 
								 | 
							
									// TempFile uses 0600 permissions
							 | 
						||
| 
								 | 
							
									f, err := ioutil.TempFile(string(d), prefix)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return "", err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if _, err := f.Write(b); err != nil {
							 | 
						||
| 
								 | 
							
										f.Close()
							 | 
						||
| 
								 | 
							
										return "", err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return f.Name(), f.Close()
							 | 
						||
| 
								 | 
							
								}
							 |