2021-03-21 15:34:50 +00:00
|
|
|
// SPDX-FileCopyrightText: 2021 Luke Granger-Brown <depot@lukegb.com>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2021-03-21 15:17:46 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-03-21 18:04:37 +00:00
|
|
|
// 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{
|
2024-10-20 16:23:36 +00:00
|
|
|
BufferSize: 8 * 1024 * 1024, /* 8 MiB */
|
2021-03-21 18:04:37 +00:00
|
|
|
CacheControl: attrs.CacheControl,
|
|
|
|
ContentDisposition: attrs.ContentDisposition,
|
|
|
|
ContentEncoding: attrs.ContentEncoding,
|
|
|
|
ContentLanguage: attrs.ContentLanguage,
|
|
|
|
ContentType: attrs.ContentType,
|
|
|
|
Metadata: attrs.Metadata,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-21 15:17:46 +00:00
|
|
|
// 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
|
|
|
|
}
|