2018-11-19 21:13:16 +00:00
|
|
|
package physical
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"strings"
|
|
|
|
"unicode"
|
|
|
|
"unicode/utf8"
|
|
|
|
)
|
|
|
|
|
2021-04-08 16:43:39 +00:00
|
|
|
var (
|
|
|
|
ErrNonUTF8 = errors.New("key contains invalid UTF-8 characters")
|
|
|
|
ErrNonPrintable = errors.New("key contains non-printable characters")
|
|
|
|
)
|
2018-11-19 21:13:16 +00:00
|
|
|
|
|
|
|
// StorageEncoding is used to add errors into underlying physical requests
|
|
|
|
type StorageEncoding struct {
|
|
|
|
Backend
|
|
|
|
}
|
|
|
|
|
|
|
|
// TransactionalStorageEncoding is the transactional version of the error
|
|
|
|
// injector
|
|
|
|
type TransactionalStorageEncoding struct {
|
|
|
|
*StorageEncoding
|
|
|
|
Transactional
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify StorageEncoding satisfies the correct interfaces
|
2021-04-08 16:43:39 +00:00
|
|
|
var (
|
|
|
|
_ Backend = (*StorageEncoding)(nil)
|
|
|
|
_ Transactional = (*TransactionalStorageEncoding)(nil)
|
|
|
|
)
|
2018-11-19 21:13:16 +00:00
|
|
|
|
|
|
|
// NewStorageEncoding returns a wrapped physical backend and verifies the key
|
|
|
|
// encoding
|
|
|
|
func NewStorageEncoding(b Backend) Backend {
|
|
|
|
enc := &StorageEncoding{
|
|
|
|
Backend: b,
|
|
|
|
}
|
|
|
|
|
|
|
|
if bTxn, ok := b.(Transactional); ok {
|
2018-11-19 23:24:41 +00:00
|
|
|
return &TransactionalStorageEncoding{
|
2018-11-19 21:13:16 +00:00
|
|
|
StorageEncoding: enc,
|
|
|
|
Transactional: bTxn,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return enc
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *StorageEncoding) containsNonPrintableChars(key string) bool {
|
|
|
|
idx := strings.IndexFunc(key, func(c rune) bool {
|
|
|
|
return !unicode.IsPrint(c)
|
|
|
|
})
|
|
|
|
|
|
|
|
return idx != -1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *StorageEncoding) Put(ctx context.Context, entry *Entry) error {
|
|
|
|
if !utf8.ValidString(entry.Key) {
|
|
|
|
return ErrNonUTF8
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.containsNonPrintableChars(entry.Key) {
|
|
|
|
return ErrNonPrintable
|
|
|
|
}
|
|
|
|
|
|
|
|
return e.Backend.Put(ctx, entry)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *StorageEncoding) Delete(ctx context.Context, key string) error {
|
|
|
|
if !utf8.ValidString(key) {
|
|
|
|
return ErrNonUTF8
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.containsNonPrintableChars(key) {
|
|
|
|
return ErrNonPrintable
|
|
|
|
}
|
|
|
|
|
|
|
|
return e.Backend.Delete(ctx, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TransactionalStorageEncoding) Transaction(ctx context.Context, txns []*TxnEntry) error {
|
|
|
|
for _, txn := range txns {
|
|
|
|
if !utf8.ValidString(txn.Entry.Key) {
|
|
|
|
return ErrNonUTF8
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.containsNonPrintableChars(txn.Entry.Key) {
|
|
|
|
return ErrNonPrintable
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return e.Transactional.Transaction(ctx, txns)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *StorageEncoding) Purge(ctx context.Context) {
|
|
|
|
if purgeable, ok := e.Backend.(ToggleablePurgemonster); ok {
|
|
|
|
purgeable.Purge(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *StorageEncoding) SetEnabled(enabled bool) {
|
|
|
|
if purgeable, ok := e.Backend.(ToggleablePurgemonster); ok {
|
|
|
|
purgeable.SetEnabled(enabled)
|
|
|
|
}
|
|
|
|
}
|