|
@@ -1,109 +1,44 @@
|
|
|
-package main
|
|
|
+package options
|
|
|
|
|
|
import (
|
|
|
- "context"
|
|
|
- "encoding/base64"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"net/http"
|
|
|
- "net/url"
|
|
|
- "regexp"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
"time"
|
|
|
|
|
|
- "github.com/imgproxy/imgproxy/v2/structdiff"
|
|
|
-)
|
|
|
-
|
|
|
-type urlOption struct {
|
|
|
- Name string
|
|
|
- Args []string
|
|
|
-}
|
|
|
-type urlOptions []urlOption
|
|
|
-
|
|
|
-type processingHeaders struct {
|
|
|
- Accept string
|
|
|
- Width string
|
|
|
- ViewportWidth string
|
|
|
- DPR string
|
|
|
-}
|
|
|
-
|
|
|
-type gravityType int
|
|
|
-
|
|
|
-const (
|
|
|
- gravityUnknown gravityType = iota
|
|
|
- gravityCenter
|
|
|
- gravityNorth
|
|
|
- gravityEast
|
|
|
- gravitySouth
|
|
|
- gravityWest
|
|
|
- gravityNorthWest
|
|
|
- gravityNorthEast
|
|
|
- gravitySouthWest
|
|
|
- gravitySouthEast
|
|
|
- gravitySmart
|
|
|
- gravityFocusPoint
|
|
|
-)
|
|
|
+ log "github.com/sirupsen/logrus"
|
|
|
|
|
|
-var gravityTypes = map[string]gravityType{
|
|
|
- "ce": gravityCenter,
|
|
|
- "no": gravityNorth,
|
|
|
- "ea": gravityEast,
|
|
|
- "so": gravitySouth,
|
|
|
- "we": gravityWest,
|
|
|
- "nowe": gravityNorthWest,
|
|
|
- "noea": gravityNorthEast,
|
|
|
- "sowe": gravitySouthWest,
|
|
|
- "soea": gravitySouthEast,
|
|
|
- "sm": gravitySmart,
|
|
|
- "fp": gravityFocusPoint,
|
|
|
-}
|
|
|
-
|
|
|
-type resizeType int
|
|
|
-
|
|
|
-const (
|
|
|
- resizeFit resizeType = iota
|
|
|
- resizeFill
|
|
|
- resizeFillDown
|
|
|
- resizeForce
|
|
|
- resizeAuto
|
|
|
+ "github.com/imgproxy/imgproxy/v2/config"
|
|
|
+ "github.com/imgproxy/imgproxy/v2/ierrors"
|
|
|
+ "github.com/imgproxy/imgproxy/v2/imagetype"
|
|
|
+ "github.com/imgproxy/imgproxy/v2/structdiff"
|
|
|
+ "github.com/imgproxy/imgproxy/v2/vips"
|
|
|
)
|
|
|
|
|
|
-var resizeTypes = map[string]resizeType{
|
|
|
- "fit": resizeFit,
|
|
|
- "fill": resizeFill,
|
|
|
- "fill-down": resizeFillDown,
|
|
|
- "force": resizeForce,
|
|
|
- "auto": resizeAuto,
|
|
|
-}
|
|
|
-
|
|
|
-type rgbColor struct{ R, G, B uint8 }
|
|
|
+const maxClientHintDPR = 8
|
|
|
|
|
|
-var hexColorRegex = regexp.MustCompile("^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
|
|
|
-
|
|
|
-const (
|
|
|
- hexColorLongFormat = "%02x%02x%02x"
|
|
|
- hexColorShortFormat = "%1x%1x%1x"
|
|
|
-)
|
|
|
+var errExpiredURL = errors.New("Expired URL")
|
|
|
|
|
|
-type gravityOptions struct {
|
|
|
- Type gravityType
|
|
|
+type GravityOptions struct {
|
|
|
+ Type GravityType
|
|
|
X, Y float64
|
|
|
}
|
|
|
|
|
|
-type extendOptions struct {
|
|
|
+type ExtendOptions struct {
|
|
|
Enabled bool
|
|
|
- Gravity gravityOptions
|
|
|
+ Gravity GravityOptions
|
|
|
}
|
|
|
|
|
|
-type cropOptions struct {
|
|
|
+type CropOptions struct {
|
|
|
Width float64
|
|
|
Height float64
|
|
|
- Gravity gravityOptions
|
|
|
+ Gravity GravityOptions
|
|
|
}
|
|
|
|
|
|
-type paddingOptions struct {
|
|
|
+type PaddingOptions struct {
|
|
|
Enabled bool
|
|
|
Top int
|
|
|
Right int
|
|
@@ -111,53 +46,53 @@ type paddingOptions struct {
|
|
|
Left int
|
|
|
}
|
|
|
|
|
|
-type trimOptions struct {
|
|
|
+type TrimOptions struct {
|
|
|
Enabled bool
|
|
|
Threshold float64
|
|
|
Smart bool
|
|
|
- Color rgbColor
|
|
|
+ Color vips.Color
|
|
|
EqualHor bool
|
|
|
EqualVer bool
|
|
|
}
|
|
|
|
|
|
-type watermarkOptions struct {
|
|
|
+type WatermarkOptions struct {
|
|
|
Enabled bool
|
|
|
Opacity float64
|
|
|
Replicate bool
|
|
|
- Gravity gravityOptions
|
|
|
+ Gravity GravityOptions
|
|
|
Scale float64
|
|
|
}
|
|
|
|
|
|
-type processingOptions struct {
|
|
|
- ResizingType resizeType
|
|
|
+type ProcessingOptions struct {
|
|
|
+ ResizingType ResizeType
|
|
|
Width int
|
|
|
Height int
|
|
|
MinWidth int
|
|
|
MinHeight int
|
|
|
Dpr float64
|
|
|
- Gravity gravityOptions
|
|
|
+ Gravity GravityOptions
|
|
|
Enlarge bool
|
|
|
- Extend extendOptions
|
|
|
- Crop cropOptions
|
|
|
- Padding paddingOptions
|
|
|
- Trim trimOptions
|
|
|
+ Extend ExtendOptions
|
|
|
+ Crop CropOptions
|
|
|
+ Padding PaddingOptions
|
|
|
+ Trim TrimOptions
|
|
|
Rotate int
|
|
|
- Format imageType
|
|
|
+ Format imagetype.Type
|
|
|
Quality int
|
|
|
MaxBytes int
|
|
|
Flatten bool
|
|
|
- Background rgbColor
|
|
|
+ Background vips.Color
|
|
|
Blur float32
|
|
|
Sharpen float32
|
|
|
StripMetadata bool
|
|
|
StripColorProfile bool
|
|
|
AutoRotate bool
|
|
|
|
|
|
- SkipProcessingFormats []imageType
|
|
|
+ SkipProcessingFormats []imagetype.Type
|
|
|
|
|
|
CacheBuster string
|
|
|
|
|
|
- Watermark watermarkOptions
|
|
|
+ Watermark WatermarkOptions
|
|
|
|
|
|
PreferWebP bool
|
|
|
EnforceWebP bool
|
|
@@ -169,107 +104,59 @@ type processingOptions struct {
|
|
|
UsedPresets []string
|
|
|
}
|
|
|
|
|
|
-const (
|
|
|
- imageURLCtxKey = ctxKey("imageUrl")
|
|
|
- processingOptionsCtxKey = ctxKey("processingOptions")
|
|
|
- urlTokenPlain = "plain"
|
|
|
- maxClientHintDPR = 8
|
|
|
-
|
|
|
- msgForbidden = "Forbidden"
|
|
|
- msgInvalidURL = "Invalid URL"
|
|
|
- msgInvalidSource = "Invalid Source"
|
|
|
- msgExpiredURL = "Expired URL"
|
|
|
-)
|
|
|
-
|
|
|
-func (gt gravityType) String() string {
|
|
|
- for k, v := range gravityTypes {
|
|
|
- if v == gt {
|
|
|
- return k
|
|
|
- }
|
|
|
- }
|
|
|
- return ""
|
|
|
-}
|
|
|
-
|
|
|
-func (gt gravityType) MarshalJSON() ([]byte, error) {
|
|
|
- for k, v := range gravityTypes {
|
|
|
- if v == gt {
|
|
|
- return []byte(fmt.Sprintf("%q", k)), nil
|
|
|
- }
|
|
|
- }
|
|
|
- return []byte("null"), nil
|
|
|
-}
|
|
|
-
|
|
|
-func (rt resizeType) String() string {
|
|
|
- for k, v := range resizeTypes {
|
|
|
- if v == rt {
|
|
|
- return k
|
|
|
- }
|
|
|
- }
|
|
|
- return ""
|
|
|
-}
|
|
|
-
|
|
|
-func (rt resizeType) MarshalJSON() ([]byte, error) {
|
|
|
- for k, v := range resizeTypes {
|
|
|
- if v == rt {
|
|
|
- return []byte(fmt.Sprintf("%q", k)), nil
|
|
|
- }
|
|
|
- }
|
|
|
- return []byte("null"), nil
|
|
|
-}
|
|
|
-
|
|
|
var (
|
|
|
- _newProcessingOptions processingOptions
|
|
|
+ _newProcessingOptions ProcessingOptions
|
|
|
newProcessingOptionsOnce sync.Once
|
|
|
)
|
|
|
|
|
|
-func newProcessingOptions() *processingOptions {
|
|
|
+func NewProcessingOptions() *ProcessingOptions {
|
|
|
newProcessingOptionsOnce.Do(func() {
|
|
|
- _newProcessingOptions = processingOptions{
|
|
|
- ResizingType: resizeFit,
|
|
|
+ _newProcessingOptions = ProcessingOptions{
|
|
|
+ ResizingType: ResizeFit,
|
|
|
Width: 0,
|
|
|
Height: 0,
|
|
|
- Gravity: gravityOptions{Type: gravityCenter},
|
|
|
+ Gravity: GravityOptions{Type: GravityCenter},
|
|
|
Enlarge: false,
|
|
|
- Extend: extendOptions{Enabled: false, Gravity: gravityOptions{Type: gravityCenter}},
|
|
|
- Padding: paddingOptions{Enabled: false},
|
|
|
- Trim: trimOptions{Enabled: false, Threshold: 10, Smart: true},
|
|
|
+ Extend: ExtendOptions{Enabled: false, Gravity: GravityOptions{Type: GravityCenter}},
|
|
|
+ Padding: PaddingOptions{Enabled: false},
|
|
|
+ Trim: TrimOptions{Enabled: false, Threshold: 10, Smart: true},
|
|
|
Rotate: 0,
|
|
|
Quality: 0,
|
|
|
MaxBytes: 0,
|
|
|
- Format: imageTypeUnknown,
|
|
|
- Background: rgbColor{255, 255, 255},
|
|
|
+ Format: imagetype.Unknown,
|
|
|
+ Background: vips.Color{R: 255, G: 255, B: 255},
|
|
|
Blur: 0,
|
|
|
Sharpen: 0,
|
|
|
Dpr: 1,
|
|
|
- Watermark: watermarkOptions{Opacity: 1, Replicate: false, Gravity: gravityOptions{Type: gravityCenter}},
|
|
|
- StripMetadata: conf.StripMetadata,
|
|
|
- StripColorProfile: conf.StripColorProfile,
|
|
|
- AutoRotate: conf.AutoRotate,
|
|
|
+ Watermark: WatermarkOptions{Opacity: 1, Replicate: false, Gravity: GravityOptions{Type: GravityCenter}},
|
|
|
+ StripMetadata: config.StripMetadata,
|
|
|
+ StripColorProfile: config.StripColorProfile,
|
|
|
+ AutoRotate: config.AutoRotate,
|
|
|
}
|
|
|
})
|
|
|
|
|
|
po := _newProcessingOptions
|
|
|
- po.SkipProcessingFormats = append([]imageType(nil), conf.SkipProcessingFormats...)
|
|
|
- po.UsedPresets = make([]string, 0, len(conf.Presets))
|
|
|
+ po.SkipProcessingFormats = append([]imagetype.Type(nil), config.SkipProcessingFormats...)
|
|
|
+ po.UsedPresets = make([]string, 0, len(config.Presets))
|
|
|
|
|
|
return &po
|
|
|
}
|
|
|
|
|
|
-func (po *processingOptions) getQuality() int {
|
|
|
+func (po *ProcessingOptions) GetQuality() int {
|
|
|
q := po.Quality
|
|
|
|
|
|
if q == 0 {
|
|
|
- q = conf.FormatQuality[po.Format]
|
|
|
+ q = config.FormatQuality[po.Format]
|
|
|
}
|
|
|
|
|
|
if q == 0 {
|
|
|
- q = conf.Quality
|
|
|
+ q = config.Quality
|
|
|
}
|
|
|
|
|
|
return q
|
|
|
}
|
|
|
|
|
|
-func (po *processingOptions) isPresetUsed(name string) bool {
|
|
|
+func (po *ProcessingOptions) isPresetUsed(name string) bool {
|
|
|
for _, usedName := range po.UsedPresets {
|
|
|
if usedName == name {
|
|
|
return true
|
|
@@ -278,109 +165,18 @@ func (po *processingOptions) isPresetUsed(name string) bool {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
-func (po *processingOptions) presetUsed(name string) {
|
|
|
- po.UsedPresets = append(po.UsedPresets, name)
|
|
|
+func (po *ProcessingOptions) Diff() structdiff.Entries {
|
|
|
+ return structdiff.Diff(NewProcessingOptions(), po)
|
|
|
}
|
|
|
|
|
|
-func (po *processingOptions) Diff() structdiff.Entries {
|
|
|
- return structdiff.Diff(newProcessingOptions(), po)
|
|
|
-}
|
|
|
-
|
|
|
-func (po *processingOptions) String() string {
|
|
|
+func (po *ProcessingOptions) String() string {
|
|
|
return po.Diff().String()
|
|
|
}
|
|
|
|
|
|
-func (po *processingOptions) MarshalJSON() ([]byte, error) {
|
|
|
+func (po *ProcessingOptions) MarshalJSON() ([]byte, error) {
|
|
|
return po.Diff().MarshalJSON()
|
|
|
}
|
|
|
|
|
|
-func colorFromHex(hexcolor string) (rgbColor, error) {
|
|
|
- c := rgbColor{}
|
|
|
-
|
|
|
- if !hexColorRegex.MatchString(hexcolor) {
|
|
|
- return c, fmt.Errorf("Invalid hex color: %s", hexcolor)
|
|
|
- }
|
|
|
-
|
|
|
- if len(hexcolor) == 3 {
|
|
|
- fmt.Sscanf(hexcolor, hexColorShortFormat, &c.R, &c.G, &c.B)
|
|
|
- c.R *= 17
|
|
|
- c.G *= 17
|
|
|
- c.B *= 17
|
|
|
- } else {
|
|
|
- fmt.Sscanf(hexcolor, hexColorLongFormat, &c.R, &c.G, &c.B)
|
|
|
- }
|
|
|
-
|
|
|
- return c, nil
|
|
|
-}
|
|
|
-
|
|
|
-func decodeBase64URL(parts []string) (string, string, error) {
|
|
|
- var format string
|
|
|
-
|
|
|
- encoded := strings.Join(parts, "")
|
|
|
- urlParts := strings.Split(encoded, ".")
|
|
|
-
|
|
|
- if len(urlParts[0]) == 0 {
|
|
|
- return "", "", errors.New("Image URL is empty")
|
|
|
- }
|
|
|
-
|
|
|
- if len(urlParts) > 2 {
|
|
|
- return "", "", fmt.Errorf("Multiple formats are specified: %s", encoded)
|
|
|
- }
|
|
|
-
|
|
|
- if len(urlParts) == 2 && len(urlParts[1]) > 0 {
|
|
|
- format = urlParts[1]
|
|
|
- }
|
|
|
-
|
|
|
- imageURL, err := base64.RawURLEncoding.DecodeString(strings.TrimRight(urlParts[0], "="))
|
|
|
- if err != nil {
|
|
|
- return "", "", fmt.Errorf("Invalid url encoding: %s", encoded)
|
|
|
- }
|
|
|
-
|
|
|
- fullURL := fmt.Sprintf("%s%s", conf.BaseURL, string(imageURL))
|
|
|
-
|
|
|
- return fullURL, format, nil
|
|
|
-}
|
|
|
-
|
|
|
-func decodePlainURL(parts []string) (string, string, error) {
|
|
|
- var format string
|
|
|
-
|
|
|
- encoded := strings.Join(parts, "/")
|
|
|
- urlParts := strings.Split(encoded, "@")
|
|
|
-
|
|
|
- if len(urlParts[0]) == 0 {
|
|
|
- return "", "", errors.New("Image URL is empty")
|
|
|
- }
|
|
|
-
|
|
|
- if len(urlParts) > 2 {
|
|
|
- return "", "", fmt.Errorf("Multiple formats are specified: %s", encoded)
|
|
|
- }
|
|
|
-
|
|
|
- if len(urlParts) == 2 && len(urlParts[1]) > 0 {
|
|
|
- format = urlParts[1]
|
|
|
- }
|
|
|
-
|
|
|
- unescaped, err := url.PathUnescape(urlParts[0])
|
|
|
- if err != nil {
|
|
|
- return "", "", fmt.Errorf("Invalid url encoding: %s", encoded)
|
|
|
- }
|
|
|
-
|
|
|
- fullURL := fmt.Sprintf("%s%s", conf.BaseURL, unescaped)
|
|
|
-
|
|
|
- return fullURL, format, nil
|
|
|
-}
|
|
|
-
|
|
|
-func decodeURL(parts []string) (string, string, error) {
|
|
|
- if len(parts) == 0 {
|
|
|
- return "", "", errors.New("Image URL is empty")
|
|
|
- }
|
|
|
-
|
|
|
- if parts[0] == urlTokenPlain && len(parts) > 1 {
|
|
|
- return decodePlainURL(parts[1:])
|
|
|
- }
|
|
|
-
|
|
|
- return decodeBase64URL(parts)
|
|
|
-}
|
|
|
-
|
|
|
func parseDimension(d *int, name, arg string) error {
|
|
|
if v, err := strconv.Atoi(arg); err == nil && v >= 0 {
|
|
|
*d = v
|
|
@@ -395,21 +191,21 @@ func parseBoolOption(str string) bool {
|
|
|
b, err := strconv.ParseBool(str)
|
|
|
|
|
|
if err != nil {
|
|
|
- logWarning("`%s` is not a valid boolean value. Treated as false", str)
|
|
|
+ log.Warningf("`%s` is not a valid boolean value. Treated as false", str)
|
|
|
}
|
|
|
|
|
|
return b
|
|
|
}
|
|
|
|
|
|
-func isGravityOffcetValid(gravity gravityType, offset float64) bool {
|
|
|
- if gravity == gravityCenter {
|
|
|
+func isGravityOffcetValid(gravity GravityType, offset float64) bool {
|
|
|
+ if gravity == GravityCenter {
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
- return offset >= 0 && (gravity != gravityFocusPoint || offset <= 1)
|
|
|
+ return offset >= 0 && (gravity != GravityFocusPoint || offset <= 1)
|
|
|
}
|
|
|
|
|
|
-func parseGravity(g *gravityOptions, args []string) error {
|
|
|
+func parseGravity(g *GravityOptions, args []string) error {
|
|
|
nArgs := len(args)
|
|
|
|
|
|
if nArgs > 3 {
|
|
@@ -422,9 +218,9 @@ func parseGravity(g *gravityOptions, args []string) error {
|
|
|
return fmt.Errorf("Invalid gravity: %s", args[0])
|
|
|
}
|
|
|
|
|
|
- if g.Type == gravitySmart && nArgs > 1 {
|
|
|
+ if g.Type == GravitySmart && nArgs > 1 {
|
|
|
return fmt.Errorf("Invalid gravity arguments: %v", args)
|
|
|
- } else if g.Type == gravityFocusPoint && nArgs != 3 {
|
|
|
+ } else if g.Type == GravityFocusPoint && nArgs != 3 {
|
|
|
return fmt.Errorf("Invalid gravity arguments: %v", args)
|
|
|
}
|
|
|
|
|
@@ -447,7 +243,7 @@ func parseGravity(g *gravityOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyWidthOption(po *processingOptions, args []string) error {
|
|
|
+func applyWidthOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid width arguments: %v", args)
|
|
|
}
|
|
@@ -455,7 +251,7 @@ func applyWidthOption(po *processingOptions, args []string) error {
|
|
|
return parseDimension(&po.Width, "width", args[0])
|
|
|
}
|
|
|
|
|
|
-func applyHeightOption(po *processingOptions, args []string) error {
|
|
|
+func applyHeightOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid height arguments: %v", args)
|
|
|
}
|
|
@@ -463,7 +259,7 @@ func applyHeightOption(po *processingOptions, args []string) error {
|
|
|
return parseDimension(&po.Height, "height", args[0])
|
|
|
}
|
|
|
|
|
|
-func applyMinWidthOption(po *processingOptions, args []string) error {
|
|
|
+func applyMinWidthOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid min width arguments: %v", args)
|
|
|
}
|
|
@@ -471,7 +267,7 @@ func applyMinWidthOption(po *processingOptions, args []string) error {
|
|
|
return parseDimension(&po.MinWidth, "min width", args[0])
|
|
|
}
|
|
|
|
|
|
-func applyMinHeightOption(po *processingOptions, args []string) error {
|
|
|
+func applyMinHeightOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid min height arguments: %v", args)
|
|
|
}
|
|
@@ -479,7 +275,7 @@ func applyMinHeightOption(po *processingOptions, args []string) error {
|
|
|
return parseDimension(&po.MinHeight, " min height", args[0])
|
|
|
}
|
|
|
|
|
|
-func applyEnlargeOption(po *processingOptions, args []string) error {
|
|
|
+func applyEnlargeOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid enlarge arguments: %v", args)
|
|
|
}
|
|
@@ -489,7 +285,7 @@ func applyEnlargeOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyExtendOption(po *processingOptions, args []string) error {
|
|
|
+func applyExtendOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 4 {
|
|
|
return fmt.Errorf("Invalid extend arguments: %v", args)
|
|
|
}
|
|
@@ -501,7 +297,7 @@ func applyExtendOption(po *processingOptions, args []string) error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- if po.Extend.Gravity.Type == gravitySmart {
|
|
|
+ if po.Extend.Gravity.Type == GravitySmart {
|
|
|
return errors.New("extend doesn't support smart gravity")
|
|
|
}
|
|
|
}
|
|
@@ -509,7 +305,7 @@ func applyExtendOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applySizeOption(po *processingOptions, args []string) (err error) {
|
|
|
+func applySizeOption(po *ProcessingOptions, args []string) (err error) {
|
|
|
if len(args) > 7 {
|
|
|
return fmt.Errorf("Invalid size arguments: %v", args)
|
|
|
}
|
|
@@ -541,7 +337,7 @@ func applySizeOption(po *processingOptions, args []string) (err error) {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyResizingTypeOption(po *processingOptions, args []string) error {
|
|
|
+func applyResizingTypeOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid resizing type arguments: %v", args)
|
|
|
}
|
|
@@ -555,7 +351,7 @@ func applyResizingTypeOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyResizeOption(po *processingOptions, args []string) error {
|
|
|
+func applyResizeOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 8 {
|
|
|
return fmt.Errorf("Invalid resize arguments: %v", args)
|
|
|
}
|
|
@@ -575,7 +371,7 @@ func applyResizeOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyDprOption(po *processingOptions, args []string) error {
|
|
|
+func applyDprOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid dpr arguments: %v", args)
|
|
|
}
|
|
@@ -589,11 +385,11 @@ func applyDprOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyGravityOption(po *processingOptions, args []string) error {
|
|
|
+func applyGravityOption(po *ProcessingOptions, args []string) error {
|
|
|
return parseGravity(&po.Gravity, args)
|
|
|
}
|
|
|
|
|
|
-func applyCropOption(po *processingOptions, args []string) error {
|
|
|
+func applyCropOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 5 {
|
|
|
return fmt.Errorf("Invalid crop arguments: %v", args)
|
|
|
}
|
|
@@ -619,7 +415,7 @@ func applyCropOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyPaddingOption(po *processingOptions, args []string) error {
|
|
|
+func applyPaddingOption(po *ProcessingOptions, args []string) error {
|
|
|
nArgs := len(args)
|
|
|
|
|
|
if nArgs < 1 || nArgs > 4 {
|
|
@@ -663,7 +459,7 @@ func applyPaddingOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyTrimOption(po *processingOptions, args []string) error {
|
|
|
+func applyTrimOption(po *ProcessingOptions, args []string) error {
|
|
|
nArgs := len(args)
|
|
|
|
|
|
if nArgs > 4 {
|
|
@@ -678,7 +474,7 @@ func applyTrimOption(po *processingOptions, args []string) error {
|
|
|
}
|
|
|
|
|
|
if nArgs > 1 && len(args[1]) > 0 {
|
|
|
- if c, err := colorFromHex(args[1]); err == nil {
|
|
|
+ if c, err := vips.ColorFromHex(args[1]); err == nil {
|
|
|
po.Trim.Color = c
|
|
|
po.Trim.Smart = false
|
|
|
} else {
|
|
@@ -697,7 +493,7 @@ func applyTrimOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyRotateOption(po *processingOptions, args []string) error {
|
|
|
+func applyRotateOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid rotate arguments: %v", args)
|
|
|
}
|
|
@@ -711,7 +507,7 @@ func applyRotateOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyQualityOption(po *processingOptions, args []string) error {
|
|
|
+func applyQualityOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid quality arguments: %v", args)
|
|
|
}
|
|
@@ -725,7 +521,7 @@ func applyQualityOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyMaxBytesOption(po *processingOptions, args []string) error {
|
|
|
+func applyMaxBytesOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid max_bytes arguments: %v", args)
|
|
|
}
|
|
@@ -739,12 +535,12 @@ func applyMaxBytesOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyBackgroundOption(po *processingOptions, args []string) error {
|
|
|
+func applyBackgroundOption(po *ProcessingOptions, args []string) error {
|
|
|
switch len(args) {
|
|
|
case 1:
|
|
|
if len(args[0]) == 0 {
|
|
|
po.Flatten = false
|
|
|
- } else if c, err := colorFromHex(args[0]); err == nil {
|
|
|
+ } else if c, err := vips.ColorFromHex(args[0]); err == nil {
|
|
|
po.Flatten = true
|
|
|
po.Background = c
|
|
|
} else {
|
|
@@ -779,7 +575,7 @@ func applyBackgroundOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyBlurOption(po *processingOptions, args []string) error {
|
|
|
+func applyBlurOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid blur arguments: %v", args)
|
|
|
}
|
|
@@ -793,7 +589,7 @@ func applyBlurOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applySharpenOption(po *processingOptions, args []string) error {
|
|
|
+func applySharpenOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid sharpen arguments: %v", args)
|
|
|
}
|
|
@@ -807,17 +603,17 @@ func applySharpenOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyPresetOption(po *processingOptions, args []string) error {
|
|
|
+func applyPresetOption(po *ProcessingOptions, args []string) error {
|
|
|
for _, preset := range args {
|
|
|
- if p, ok := conf.Presets[preset]; ok {
|
|
|
+ if p, ok := presets[preset]; ok {
|
|
|
if po.isPresetUsed(preset) {
|
|
|
- logWarning("Recursive preset usage is detected: %s", preset)
|
|
|
+ log.Warningf("Recursive preset usage is detected: %s", preset)
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
- po.presetUsed(preset)
|
|
|
+ po.UsedPresets = append(po.UsedPresets, preset)
|
|
|
|
|
|
- if err := applyProcessingOptions(po, p); err != nil {
|
|
|
+ if err := applyURLOptions(po, p); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
} else {
|
|
@@ -828,7 +624,7 @@ func applyPresetOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyWatermarkOption(po *processingOptions, args []string) error {
|
|
|
+func applyWatermarkOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 7 {
|
|
|
return fmt.Errorf("Invalid watermark arguments: %v", args)
|
|
|
}
|
|
@@ -843,7 +639,7 @@ func applyWatermarkOption(po *processingOptions, args []string) error {
|
|
|
if len(args) > 1 && len(args[1]) > 0 {
|
|
|
if args[1] == "re" {
|
|
|
po.Watermark.Replicate = true
|
|
|
- } else if g, ok := gravityTypes[args[1]]; ok && g != gravityFocusPoint && g != gravitySmart {
|
|
|
+ } else if g, ok := gravityTypes[args[1]]; ok && g != GravityFocusPoint && g != GravitySmart {
|
|
|
po.Watermark.Gravity.Type = g
|
|
|
} else {
|
|
|
return fmt.Errorf("Invalid watermark position: %s", args[1])
|
|
@@ -877,25 +673,21 @@ func applyWatermarkOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyFormatOption(po *processingOptions, args []string) error {
|
|
|
+func applyFormatOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid format arguments: %v", args)
|
|
|
}
|
|
|
|
|
|
- if f, ok := imageTypes[args[0]]; ok {
|
|
|
+ if f, ok := imagetype.Types[args[0]]; ok {
|
|
|
po.Format = f
|
|
|
} else {
|
|
|
return fmt.Errorf("Invalid image format: %s", args[0])
|
|
|
}
|
|
|
|
|
|
- if !imageTypeSaveSupport(po.Format) {
|
|
|
- return fmt.Errorf("Resulting image format is not supported: %s", po.Format)
|
|
|
- }
|
|
|
-
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyCacheBusterOption(po *processingOptions, args []string) error {
|
|
|
+func applyCacheBusterOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid cache buster arguments: %v", args)
|
|
|
}
|
|
@@ -905,9 +697,9 @@ func applyCacheBusterOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applySkipProcessingFormatsOption(po *processingOptions, args []string) error {
|
|
|
+func applySkipProcessingFormatsOption(po *ProcessingOptions, args []string) error {
|
|
|
for _, format := range args {
|
|
|
- if f, ok := imageTypes[format]; ok {
|
|
|
+ if f, ok := imagetype.Types[format]; ok {
|
|
|
po.SkipProcessingFormats = append(po.SkipProcessingFormats, f)
|
|
|
} else {
|
|
|
return fmt.Errorf("Invalid image format in skip processing: %s", format)
|
|
@@ -917,7 +709,7 @@ func applySkipProcessingFormatsOption(po *processingOptions, args []string) erro
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyFilenameOption(po *processingOptions, args []string) error {
|
|
|
+func applyFilenameOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid filename arguments: %v", args)
|
|
|
}
|
|
@@ -927,7 +719,7 @@ func applyFilenameOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyExpiresOption(po *processingOptions, args []string) error {
|
|
|
+func applyExpiresOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid expires arguments: %v", args)
|
|
|
}
|
|
@@ -938,13 +730,13 @@ func applyExpiresOption(po *processingOptions, args []string) error {
|
|
|
}
|
|
|
|
|
|
if timestamp > 0 && timestamp < time.Now().Unix() {
|
|
|
- return errors.New(msgExpiredURL)
|
|
|
+ return errExpiredURL
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyStripMetadataOption(po *processingOptions, args []string) error {
|
|
|
+func applyStripMetadataOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid strip metadata arguments: %v", args)
|
|
|
}
|
|
@@ -954,7 +746,7 @@ func applyStripMetadataOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyStripColorProfileOption(po *processingOptions, args []string) error {
|
|
|
+func applyStripColorProfileOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid strip color profile arguments: %v", args)
|
|
|
}
|
|
@@ -964,7 +756,7 @@ func applyStripColorProfileOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyAutoRotateOption(po *processingOptions, args []string) error {
|
|
|
+func applyAutoRotateOption(po *ProcessingOptions, args []string) error {
|
|
|
if len(args) > 1 {
|
|
|
return fmt.Errorf("Invalid auto rotate arguments: %v", args)
|
|
|
}
|
|
@@ -974,7 +766,7 @@ func applyAutoRotateOption(po *processingOptions, args []string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func applyProcessingOption(po *processingOptions, name string, args []string) error {
|
|
|
+func applyURLOption(po *ProcessingOptions, name string, args []string) error {
|
|
|
switch name {
|
|
|
case "resize", "rs":
|
|
|
return applyResizeOption(po, args)
|
|
@@ -1044,9 +836,9 @@ func applyProcessingOption(po *processingOptions, name string, args []string) er
|
|
|
return fmt.Errorf("Unknown processing option: %s", name)
|
|
|
}
|
|
|
|
|
|
-func applyProcessingOptions(po *processingOptions, options urlOptions) error {
|
|
|
+func applyURLOptions(po *ProcessingOptions, options urlOptions) error {
|
|
|
for _, opt := range options {
|
|
|
- if err := applyProcessingOption(po, opt.Name, opt.Args); err != nil {
|
|
|
+ if err := applyURLOption(po, opt.Name, opt.Args); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
@@ -1054,73 +846,40 @@ func applyProcessingOptions(po *processingOptions, options urlOptions) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func isAllowedSource(imageURL string) bool {
|
|
|
- if len(conf.AllowedSources) == 0 {
|
|
|
- return true
|
|
|
- }
|
|
|
- for _, val := range conf.AllowedSources {
|
|
|
- if strings.HasPrefix(imageURL, string(val)) {
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
- return false
|
|
|
-}
|
|
|
+func defaultProcessingOptions(headers http.Header) (*ProcessingOptions, error) {
|
|
|
+ po := NewProcessingOptions()
|
|
|
|
|
|
-func parseURLOptions(opts []string) (urlOptions, []string) {
|
|
|
- parsed := make(urlOptions, 0, len(opts))
|
|
|
- urlStart := len(opts) + 1
|
|
|
+ headerAccept := headers.Get("Accept")
|
|
|
|
|
|
- for i, opt := range opts {
|
|
|
- args := strings.Split(opt, ":")
|
|
|
-
|
|
|
- if len(args) == 1 {
|
|
|
- urlStart = i
|
|
|
- break
|
|
|
- }
|
|
|
-
|
|
|
- parsed = append(parsed, urlOption{Name: args[0], Args: args[1:]})
|
|
|
+ if strings.Contains(headerAccept, "image/webp") {
|
|
|
+ po.PreferWebP = config.EnableWebpDetection || config.EnforceWebp
|
|
|
+ po.EnforceWebP = config.EnforceWebp
|
|
|
}
|
|
|
|
|
|
- var rest []string
|
|
|
-
|
|
|
- if urlStart < len(opts) {
|
|
|
- rest = opts[urlStart:]
|
|
|
- } else {
|
|
|
- rest = []string{}
|
|
|
+ if strings.Contains(headerAccept, "image/avif") {
|
|
|
+ po.PreferAvif = config.EnableAvifDetection || config.EnforceAvif
|
|
|
+ po.EnforceAvif = config.EnforceAvif
|
|
|
}
|
|
|
|
|
|
- return parsed, rest
|
|
|
-}
|
|
|
-
|
|
|
-func defaultProcessingOptions(headers *processingHeaders) (*processingOptions, error) {
|
|
|
- po := newProcessingOptions()
|
|
|
-
|
|
|
- if strings.Contains(headers.Accept, "image/webp") {
|
|
|
- po.PreferWebP = conf.EnableWebpDetection || conf.EnforceWebp
|
|
|
- po.EnforceWebP = conf.EnforceWebp
|
|
|
- }
|
|
|
-
|
|
|
- if strings.Contains(headers.Accept, "image/avif") {
|
|
|
- po.PreferAvif = conf.EnableAvifDetection || conf.EnforceAvif
|
|
|
- po.EnforceAvif = conf.EnforceAvif
|
|
|
- }
|
|
|
-
|
|
|
- if conf.EnableClientHints && len(headers.ViewportWidth) > 0 {
|
|
|
- if vw, err := strconv.Atoi(headers.ViewportWidth); err == nil {
|
|
|
- po.Width = vw
|
|
|
+ if config.EnableClientHints {
|
|
|
+ if headerViewportWidth := headers.Get("Viewport-Width"); len(headerViewportWidth) > 0 {
|
|
|
+ if vw, err := strconv.Atoi(headerViewportWidth); err == nil {
|
|
|
+ po.Width = vw
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- if conf.EnableClientHints && len(headers.Width) > 0 {
|
|
|
- if w, err := strconv.Atoi(headers.Width); err == nil {
|
|
|
- po.Width = w
|
|
|
+ if headerWidth := headers.Get("Width"); len(headerWidth) > 0 {
|
|
|
+ if w, err := strconv.Atoi(headerWidth); err == nil {
|
|
|
+ po.Width = w
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- if conf.EnableClientHints && len(headers.DPR) > 0 {
|
|
|
- if dpr, err := strconv.ParseFloat(headers.DPR, 64); err == nil && (dpr > 0 && dpr <= maxClientHintDPR) {
|
|
|
- po.Dpr = dpr
|
|
|
+ if headerDPR := headers.Get("DPR"); len(headerDPR) > 0 {
|
|
|
+ if dpr, err := strconv.ParseFloat(headerDPR, 64); err == nil && (dpr > 0 && dpr <= maxClientHintDPR) {
|
|
|
+ po.Dpr = dpr
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- if _, ok := conf.Presets["default"]; ok {
|
|
|
+
|
|
|
+ if _, ok := presets["default"]; ok {
|
|
|
if err := applyPresetOption(po, []string{"default"}); err != nil {
|
|
|
return po, err
|
|
|
}
|
|
@@ -1129,117 +888,81 @@ func defaultProcessingOptions(headers *processingHeaders) (*processingOptions, e
|
|
|
return po, nil
|
|
|
}
|
|
|
|
|
|
-func parsePathOptions(parts []string, headers *processingHeaders) (string, *processingOptions, error) {
|
|
|
+func parsePathOptions(parts []string, headers http.Header) (*ProcessingOptions, string, error) {
|
|
|
po, err := defaultProcessingOptions(headers)
|
|
|
if err != nil {
|
|
|
- return "", po, err
|
|
|
+ return nil, "", err
|
|
|
}
|
|
|
|
|
|
options, urlParts := parseURLOptions(parts)
|
|
|
|
|
|
- if err = applyProcessingOptions(po, options); err != nil {
|
|
|
- return "", po, err
|
|
|
+ if err = applyURLOptions(po, options); err != nil {
|
|
|
+ return nil, "", err
|
|
|
}
|
|
|
|
|
|
- url, extension, err := decodeURL(urlParts)
|
|
|
+ url, extension, err := DecodeURL(urlParts)
|
|
|
if err != nil {
|
|
|
- return "", po, err
|
|
|
+ return nil, "", err
|
|
|
}
|
|
|
|
|
|
if len(extension) > 0 {
|
|
|
if err = applyFormatOption(po, []string{extension}); err != nil {
|
|
|
- return "", po, err
|
|
|
+ return nil, "", err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return url, po, nil
|
|
|
+ return po, url, nil
|
|
|
}
|
|
|
|
|
|
-func parsePathPresets(parts []string, headers *processingHeaders) (string, *processingOptions, error) {
|
|
|
+func parsePathPresets(parts []string, headers http.Header) (*ProcessingOptions, string, error) {
|
|
|
po, err := defaultProcessingOptions(headers)
|
|
|
if err != nil {
|
|
|
- return "", po, err
|
|
|
+ return nil, "", err
|
|
|
}
|
|
|
|
|
|
presets := strings.Split(parts[0], ":")
|
|
|
urlParts := parts[1:]
|
|
|
|
|
|
if err = applyPresetOption(po, presets); err != nil {
|
|
|
- return "", nil, err
|
|
|
+ return nil, "", err
|
|
|
}
|
|
|
|
|
|
- url, extension, err := decodeURL(urlParts)
|
|
|
+ url, extension, err := DecodeURL(urlParts)
|
|
|
if err != nil {
|
|
|
- return "", po, err
|
|
|
+ return nil, "", err
|
|
|
}
|
|
|
|
|
|
if len(extension) > 0 {
|
|
|
if err = applyFormatOption(po, []string{extension}); err != nil {
|
|
|
- return "", po, err
|
|
|
+ return nil, "", err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return url, po, nil
|
|
|
+ return po, url, nil
|
|
|
}
|
|
|
|
|
|
-func parsePath(ctx context.Context, r *http.Request) (context.Context, error) {
|
|
|
- var err error
|
|
|
-
|
|
|
- path := trimAfter(r.RequestURI, '?')
|
|
|
-
|
|
|
- if len(conf.PathPrefix) > 0 {
|
|
|
- path = strings.TrimPrefix(path, conf.PathPrefix)
|
|
|
+func ParsePath(path string, headers http.Header) (*ProcessingOptions, string, error) {
|
|
|
+ if path == "" || path == "/" {
|
|
|
+ return nil, "", ierrors.New(404, fmt.Sprintf("Invalid path: %s", path), "Invalid URL")
|
|
|
}
|
|
|
|
|
|
- path = strings.TrimPrefix(path, "/")
|
|
|
+ parts := strings.Split(strings.TrimPrefix(path, "/"), "/")
|
|
|
|
|
|
- parts := strings.Split(path, "/")
|
|
|
+ var (
|
|
|
+ imageURL string
|
|
|
+ po *ProcessingOptions
|
|
|
+ err error
|
|
|
+ )
|
|
|
|
|
|
- if len(parts) < 2 {
|
|
|
- return ctx, newError(404, fmt.Sprintf("Invalid path: %s", path), msgInvalidURL)
|
|
|
- }
|
|
|
-
|
|
|
- if !conf.AllowInsecure {
|
|
|
- if err = validatePath(parts[0], strings.TrimPrefix(path, parts[0])); err != nil {
|
|
|
- return ctx, newError(403, err.Error(), msgForbidden)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- headers := &processingHeaders{
|
|
|
- Accept: r.Header.Get("Accept"),
|
|
|
- Width: r.Header.Get("Width"),
|
|
|
- ViewportWidth: r.Header.Get("Viewport-Width"),
|
|
|
- DPR: r.Header.Get("DPR"),
|
|
|
- }
|
|
|
-
|
|
|
- var imageURL string
|
|
|
- var po *processingOptions
|
|
|
-
|
|
|
- if conf.OnlyPresets {
|
|
|
- imageURL, po, err = parsePathPresets(parts[1:], headers)
|
|
|
+ if config.OnlyPresets {
|
|
|
+ po, imageURL, err = parsePathPresets(parts, headers)
|
|
|
} else {
|
|
|
- imageURL, po, err = parsePathOptions(parts[1:], headers)
|
|
|
+ po, imageURL, err = parsePathOptions(parts, headers)
|
|
|
}
|
|
|
|
|
|
if err != nil {
|
|
|
- return ctx, newError(404, err.Error(), msgInvalidURL)
|
|
|
+ return nil, "", ierrors.New(404, err.Error(), "Invalid URL")
|
|
|
}
|
|
|
|
|
|
- if !isAllowedSource(imageURL) {
|
|
|
- return ctx, newError(404, "Invalid source", msgInvalidSource)
|
|
|
- }
|
|
|
-
|
|
|
- ctx = context.WithValue(ctx, imageURLCtxKey, imageURL)
|
|
|
- ctx = context.WithValue(ctx, processingOptionsCtxKey, po)
|
|
|
-
|
|
|
- return ctx, nil
|
|
|
-}
|
|
|
-
|
|
|
-func getImageURL(ctx context.Context) string {
|
|
|
- str, _ := ctx.Value(imageURLCtxKey).(string)
|
|
|
- return str
|
|
|
-}
|
|
|
-
|
|
|
-func getProcessingOptions(ctx context.Context) *processingOptions {
|
|
|
- return ctx.Value(processingOptionsCtxKey).(*processingOptions)
|
|
|
+ return po, imageURL, nil
|
|
|
}
|