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