Files
go-omni/storage/s3/read.go

54 lines
1.8 KiB
Go

package s3
import (
"context"
"errors"
"net/url"
"strconv"
"time"
"git.yuntuo.hk/k2safe/go-omni/storage"
)
func (s *Store) PresignGetWithOptions(ctx context.Context, key string, ttl time.Duration, options storage.ReadOptions) (storage.ReadLink, error) {
if err := storage.Check(ctx, key); err != nil {
return storage.ReadLink{}, err
}
if ttl < time.Second || ttl > time.Hour {
return storage.ReadLink{}, errors.New("storage/s3: TTL must be 1s..1h")
}
headers, err := options.ResponseHeaders()
if err != nil {
return storage.ReadLink{}, err
}
query := url.Values{"response-content-disposition": {headers.Get("Content-Disposition")}, "response-cache-control": {headers.Get("Cache-Control")}}
if contentType := headers.Get("Content-Type"); contentType != "" {
query.Set("response-content-type", contentType)
}
u, err := s.client.PresignedGetObject(ctx, s.bucket, key, ttl, query)
if err != nil {
return storage.ReadLink{}, err
}
// Derive the exact expiry from the SDK's signed URL, not a second clock read
// around credential discovery or bucket-region lookup.
q := u.Query()
var expires time.Time
if q.Get("X-Amz-Signature") != "" {
start, e := time.Parse("20060102T150405Z", q.Get("X-Amz-Date"))
seconds, e2 := strconv.ParseInt(q.Get("X-Amz-Expires"), 10, 64)
if e != nil || e2 != nil || seconds < 1 || seconds > 3600 {
return storage.ReadLink{}, errors.New("storage/s3: invalid signed expiry")
}
expires = start.Add(time.Duration(seconds) * time.Second)
} else {
seconds, e := strconv.ParseInt(q.Get("Expires"), 10, 64)
if e != nil || q.Get("Signature") == "" {
return storage.ReadLink{}, errors.New("storage/s3: missing signed expiry")
}
expires = time.Unix(seconds, 0).UTC()
}
return storage.ReadLink{URL: u.String(), ExpiresAt: expires}, nil
}
var _ storage.ReadPresigner = (*Store)(nil)