check / check (push) Canceled after 0s
The image request now holds only its source and its transform, exactly what the variant key covers. The /v1/image/ handler checks the allowlist and verifies the signature itself with Signer.Verify(req, sig, expires). Both image handlers keep the URL's expiry as a local value for Cache-Control. The image service holds no signer, allowlist or AllowHTTP, and ValidateRequest is gone. The fetcher takes the source and builds its URL with Source.URL from its own AllowHTTP, so no URL string is built and parsed again. chooseAutoFormat returns the format it chooses instead of writing into the request. The signed URL format and every route's answers are unchanged. Model: opus-5-5
138 lines
3.9 KiB
Go
138 lines
3.9 KiB
Go
// Package imagerequest holds the image request type, its parts, its checks,
|
|
// the parsing of the /v1/image/ path, the variant key, the source key and the
|
|
// source's upstream URL. 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"
|
|
)
|
|
|
|
// 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. It holds exactly what the variant key
|
|
// covers.
|
|
type Request struct {
|
|
// Source is what to fetch
|
|
Source Source
|
|
// Transform is what to do to it
|
|
Transform Transform
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// URL returns the source's upstream URL, with the scheme http when allowHTTP
|
|
// is true and https otherwise. net/url writes its path percent-encoded again,
|
|
// so each source path is one upstream path. Its query is the source query as
|
|
// it is, with the characters escapeQuery encodes percent-encoded.
|
|
func (s Source) URL(allowHTTP bool) *url.URL {
|
|
scheme := "https"
|
|
if allowHTTP {
|
|
scheme = "http"
|
|
}
|
|
|
|
return &url.URL{
|
|
Scheme: scheme,
|
|
Host: s.Host,
|
|
Path: s.Path,
|
|
RawQuery: escapeQuery(s.Query),
|
|
}
|
|
}
|