check / check (push) Canceled after 0s
A source is now one source_content row and one file named by its SourceKey, the SHA-256 of its host, path and query, made as the variant key is. A store writes the file, then upserts the row, and removes the file when the row cannot be written; an eviction deletes the row, then the file, with no lock. source_metadata, the JSON files under cache/metadata/, MetadataStorage, the content lock, the eviction transaction and its test hook are gone, as is the reconciliation pass that dropped a source row whose file is missing, so TestReconciliationReadsAPageAtATime now counts 15 rows read, not 20. Two source URLs serving the same bytes store them twice. The tests of the removed sharing and lock go with them; the two eviction cancellation tests no longer use the hook. Model: opus-5-5
148 lines
4.2 KiB
Go
148 lines
4.2 KiB
Go
// Package imagerequest holds the image request type, its parts, its checks,
|
|
// the parsing of the /v1/image/ path, the variant key and the source key. It
|
|
// imports no other package of this repo, so every package that handles an
|
|
// image request can import it.
|
|
package imagerequest
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"time"
|
|
)
|
|
|
|
// ErrInvalidFitMode is returned when an invalid fit mode is provided.
|
|
var ErrInvalidFitMode = errors.New("invalid fit mode")
|
|
|
|
// Format represents supported output image formats.
|
|
type Format string
|
|
|
|
// Supported image output formats.
|
|
const (
|
|
FormatOriginal Format = "orig"
|
|
FormatJPEG Format = "jpeg"
|
|
FormatPNG Format = "png"
|
|
FormatWebP Format = "webp"
|
|
FormatAVIF Format = "avif"
|
|
FormatJXL Format = "jxl"
|
|
FormatGIF Format = "gif"
|
|
|
|
// FormatAuto stands for JPEG XL, AVIF, WebP or JPEG, chosen for each
|
|
// request from its Accept header once the URL's signature or token has
|
|
// been checked; it is never processed or cached as itself.
|
|
FormatAuto Format = "auto"
|
|
)
|
|
|
|
// Size represents requested image dimensions
|
|
type Size struct {
|
|
Width int
|
|
Height int
|
|
}
|
|
|
|
// OriginalSize returns true if this represents "keep original size"
|
|
func (s Size) OriginalSize() bool {
|
|
return s.Width == 0 && s.Height == 0
|
|
}
|
|
|
|
// Fit represents how to fit image into requested dimensions.
|
|
type Fit string
|
|
|
|
// Supported image fit modes.
|
|
const (
|
|
FitCover Fit = "cover"
|
|
FitContain Fit = "contain"
|
|
FitFill Fit = "fill"
|
|
FitInside Fit = "inside"
|
|
FitOutside Fit = "outside"
|
|
)
|
|
|
|
// ValidateFitMode checks if the given fit mode is valid.
|
|
// Returns ErrInvalidFitMode for unrecognized fit modes.
|
|
func ValidateFitMode(fit Fit) error {
|
|
switch fit {
|
|
case FitCover, FitContain, FitFill, FitInside, FitOutside, "":
|
|
return nil
|
|
default:
|
|
return ErrInvalidFitMode
|
|
}
|
|
}
|
|
|
|
// ValidateImageRequest checks a request's width and height with
|
|
// ValidateDimension and its fit mode with ValidateFitMode. Both the plain
|
|
// /v1/image/ route and the encrypted /v1/e/ route validate through this
|
|
// function so a request from either source enforces identical bounds.
|
|
func ValidateImageRequest(req *Request) error {
|
|
err := ValidateDimension("width", req.Transform.Size.Width)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = ValidateDimension("height", req.Transform.Size.Height)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ValidateFitMode(req.Transform.FitMode)
|
|
}
|
|
|
|
// Request represents a request for a processed image: the source to fetch
|
|
// and the transform to apply to it.
|
|
type Request struct {
|
|
// Source is what to fetch
|
|
Source Source
|
|
// Transform is what to do to it
|
|
Transform Transform
|
|
// Signature is the HMAC signature for non-allowlisted hosts
|
|
Signature string
|
|
// Expires is when the URL expires: the exp of a signed URL, or the expiry
|
|
// of an encrypted URL; the zero time if it has none
|
|
Expires time.Time
|
|
// AllowHTTP indicates whether HTTP (non-TLS) is allowed for this request
|
|
AllowHTTP bool
|
|
}
|
|
|
|
// Source is the image a request fetches from the origin.
|
|
type Source struct {
|
|
// Host is the origin host (e.g., "cdn.example.com")
|
|
Host string
|
|
// Path is the path on the origin with its percent-escapes decoded, as
|
|
// url.URL's Path holds it (e.g., "/photos/my cat.jpg")
|
|
Path string
|
|
// Query is the optional query string for the origin URL as the URL
|
|
// writes it, percent-escapes included, as url.URL's RawQuery holds it
|
|
Query string
|
|
}
|
|
|
|
// Transform is what a request does to its source; the image processor
|
|
// takes it alone.
|
|
type Transform struct {
|
|
// Size is the requested output dimensions
|
|
Size Size
|
|
// Format is the requested output format
|
|
Format Format
|
|
// Quality is the output quality (1-100) for lossy formats
|
|
Quality int
|
|
// FitMode is how to fit the image into requested dimensions
|
|
FitMode Fit
|
|
}
|
|
|
|
// SourceURL returns the full upstream URL to fetch: the source path
|
|
// percent-encoded again, as net/url writes a path, so each source path is one
|
|
// upstream path, and the source query as it is, with the characters
|
|
// escapeQuery encodes percent-encoded. Uses http:// scheme when AllowHTTP is
|
|
// true, otherwise https://.
|
|
func (r *Request) SourceURL() string {
|
|
scheme := "https"
|
|
if r.AllowHTTP {
|
|
scheme = "http"
|
|
}
|
|
|
|
sourceURL := url.URL{
|
|
Scheme: scheme,
|
|
Host: r.Source.Host,
|
|
Path: r.Source.Path,
|
|
RawQuery: escapeQuery(r.Source.Query),
|
|
}
|
|
|
|
return sourceURL.String()
|
|
}
|