The new package internal/imagerequest holds the request type, its two parts Source and Transform, the Format and Fit types, the dimension and fit checks, the /v1/image/ path parsing and the variant key; it imports no other package of pixa. imgcache.ParsedURL, imgcache.ImageRequest, signature.Request, imageprocessor.Request, the second Size, format and fit types and the conversions between them are gone. The image processor takes a Transform only. encurl keeps a private struct with the token's CBOR field names, so tokens made before still decode. VariantKey moves with the function that makes it. Tests follow the moved and renamed code; the tests of the removed conversions go with them. Model: opus-5-5
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
// Package imagerequest holds the image request type, its parts, its checks,
|
||||
// the parsing of the /v1/image/ path and the variant 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()
|
||||
}
|
||||
Reference in New Issue
Block a user