69 lines
1.7 KiB
Go
69 lines
1.7 KiB
Go
// Package storage defines streaming object storage; callers own returned bodies.
|
|
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
var ErrNotFound = errors.New("storage: object not found")
|
|
var ErrTooLarge = errors.New("storage: object exceeds limit")
|
|
|
|
type Info struct {
|
|
Key string `json:"key"`
|
|
Size int64 `json:"size"`
|
|
ContentType string `json:"contentType"`
|
|
ModifiedAt time.Time `json:"modifiedAt"`
|
|
}
|
|
type Object struct {
|
|
Info
|
|
Body io.ReadCloser
|
|
}
|
|
type Store interface {
|
|
Put(context.Context, string, io.Reader, int64, string) (Info, error)
|
|
Get(context.Context, string) (Object, error)
|
|
Delete(context.Context, string) error
|
|
}
|
|
|
|
// Presigner is an optional capability. URLs grant temporary read access and
|
|
// must not be logged. Providers may expire them earlier when credentials expire.
|
|
type Presigner interface {
|
|
PresignGet(context.Context, string, time.Duration) (*url.URL, error)
|
|
}
|
|
|
|
func Check(ctx context.Context, key string) error {
|
|
if ctx == nil {
|
|
return errors.New("storage: nil context")
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
if key == "" || len(key) > 1024 || !utf8.ValidString(key) || strings.Contains(key, "\\") || strings.IndexFunc(key, unicode.IsControl) >= 0 {
|
|
return errors.New("storage: invalid key")
|
|
}
|
|
for _, part := range strings.Split(key, "/") {
|
|
if part == "" || part == "." || part == ".." {
|
|
return errors.New("storage: invalid key path")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type ContextReader struct {
|
|
Context context.Context
|
|
Reader io.Reader
|
|
}
|
|
|
|
func (r ContextReader) Read(p []byte) (int, error) {
|
|
if err := r.Context.Err(); err != nil {
|
|
return 0, err
|
|
}
|
|
return r.Reader.Read(p)
|
|
}
|