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