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. Two source URLs serving the same bytes store them twice. 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()
|
|
}
|