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() | ||
|  | } |