// SPDX-FileCopyrightText: 2021 Luke Granger-Brown // // SPDX-License-Identifier: Apache-2.0 package fuphttp import ( "context" "fmt" "log" "strconv" "time" "gocloud.dev/blob" "gocloud.dev/gcerrors" ) type Metadata struct { // ExpiresAt is the expiry time of the object. // May be the zero time, if this object does not expire. // Stored in expires-at. ExpiresAt time.Time // Attributes is the underlying gocloud Attributes. Attributes *blob.Attributes } // WriterOptions returns a new WriterOptions based on the provided metadata. func (m *Metadata) WriterOptions() *blob.WriterOptions { attrs := m.Attributes if attrs == nil { attrs = &blob.Attributes{} } if attrs.Metadata == nil { attrs.Metadata = make(map[string]string) } if !m.ExpiresAt.IsZero() { attrs.Metadata["expires-at"] = strconv.FormatInt(m.ExpiresAt.Unix(), 10) } return &blob.WriterOptions{ CacheControl: attrs.CacheControl, ContentDisposition: attrs.ContentDisposition, ContentEncoding: attrs.ContentEncoding, ContentLanguage: attrs.ContentLanguage, ContentType: attrs.ContentType, Metadata: attrs.Metadata, } } // metadata retrieves the Metadata for the object. // Note: if the object is expired, it will delete it. func metadata(ctx context.Context, bucket *blob.Bucket, filename string) (*Metadata, error) { attrs, err := bucket.Attributes(ctx, filename) if err != nil { return nil, fmt.Errorf("Attributes(%q): %w", filename, err) } m := Metadata{ Attributes: attrs, } var exp time.Time if expStr, ok := attrs.Metadata["expires-at"]; ok { // Check for expiry. expSec, err := strconv.ParseInt(expStr, 10, 64) if err != nil { return nil, fmt.Errorf("parsing expiration %q for %q: %v", expStr, filename, err) } exp = time.Unix(expSec, 0) if exp.Before(time.Now()) { // This file has expired. Delete it from the backing storage. err := bucket.Delete(ctx, filename) switch errorCode(err) { case gcerrors.NotFound: // Something probably deleted it before we could get there. case gcerrors.OK: // This was fine. default: log.Printf("deleting expired file %q: %v", filename, err) } return nil, &fupError{ Code: gcerrors.NotFound, } } m.ExpiresAt = exp } return &m, nil }