Add URL replacements

This commit is contained in:
DarthSim
2023-05-15 20:06:46 +03:00
parent 23dedc6de3
commit bc5ca9a344
6 changed files with 75 additions and 6 deletions

View File

@@ -1,6 +1,8 @@
# Changelog # Changelog
## [Unreleased] ## [Unreleased]
### Add
- Add `IMGPROXY_URL_REPLACEMENTS` config.
## [3.17.0] - 2023-05-10 ## [3.17.0] - 2023-05-10
### Add ### Add

View File

@@ -126,7 +126,8 @@ var (
LastModifiedEnabled bool LastModifiedEnabled bool
BaseURL string BaseURL string
URLReplacements map[*regexp.Regexp]string
Presets []string Presets []string
OnlyPresets bool OnlyPresets bool
@@ -317,6 +318,7 @@ func Reset() {
LastModifiedEnabled = false LastModifiedEnabled = false
BaseURL = "" BaseURL = ""
URLReplacements = make(map[*regexp.Regexp]string)
Presets = make([]string, 0) Presets = make([]string, 0)
OnlyPresets = false OnlyPresets = false
@@ -518,6 +520,9 @@ func Configure() error {
configurators.Bool(&LastModifiedEnabled, "IMGPROXY_USE_LAST_MODIFIED") configurators.Bool(&LastModifiedEnabled, "IMGPROXY_USE_LAST_MODIFIED")
configurators.String(&BaseURL, "IMGPROXY_BASE_URL") configurators.String(&BaseURL, "IMGPROXY_BASE_URL")
if err := configurators.Replacements(&URLReplacements, "IMGPROXY_URL_REPLACEMENTS"); err != nil {
return err
}
configurators.StringSlice(&Presets, "IMGPROXY_PRESETS") configurators.StringSlice(&Presets, "IMGPROXY_PRESETS")
if err := configurators.StringSliceFile(&Presets, presetsPath); err != nil { if err := configurators.StringSliceFile(&Presets, presetsPath); err != nil {

View File

@@ -221,6 +221,26 @@ func Patterns(s *[]*regexp.Regexp, name string) {
} }
} }
func Replacements(m *map[*regexp.Regexp]string, name string) error {
var sm map[string]string
if err := StringMap(&sm, name); err != nil {
return err
}
if len(sm) > 0 {
mm := make(map[*regexp.Regexp]string)
for k, v := range sm {
mm[RegexpFromPattern(k)] = v
}
*m = mm
}
return nil
}
func RegexpFromPattern(pattern string) *regexp.Regexp { func RegexpFromPattern(pattern string) *regexp.Regexp {
var result strings.Builder var result strings.Builder
// Perform prefix matching // Perform prefix matching
@@ -228,7 +248,7 @@ func RegexpFromPattern(pattern string) *regexp.Regexp {
for i, part := range strings.Split(pattern, "*") { for i, part := range strings.Split(pattern, "*") {
// Add a regexp match all without slashes for each wildcard character // Add a regexp match all without slashes for each wildcard character
if i > 0 { if i > 0 {
result.WriteString("[^/]*") result.WriteString("([^/]*)")
} }
// Quote other parts of the pattern // Quote other parts of the pattern

View File

@@ -406,6 +406,16 @@ imgproxy can process files from OpenStack Object Storage, but this feature is di
Check out the [Serving files from OpenStack Object Storage](serving_files_from_openstack_swift.md) guide to learn more. Check out the [Serving files from OpenStack Object Storage](serving_files_from_openstack_swift.md) guide to learn more.
## Source image URLs
* `IMGPROXY_BASE_URL`: a base URL prefix that will be added to each source image URL. For example, if the base URL is `http://example.com/images` and `/path/to/image.png` is requested, imgproxy will download the source image from `http://example.com/images/path/to/image.png`. If the image URL already contains the prefix, it won't be added. Default: blank
* `IMGPROXY_URL_REPLACEMENTS`: a list of `pattern=replacement` pairs, semicolon (`;`) divided. imgproxy will replace source URL prefixes matching the pattern with the corresponding replacement. Wildcards can be included in patterns with `*` to match all characters except `/`. `${N}` in replacement strings will be replaced with wildcard values, where `N` is the number of the wildcard. Examples:
* `mys3://=s3://my_bucket/images/` will replace `mys3://image01.jpg` with `s3://my_bucket/images/image01.jpg`
* `mys3://*/=s3://my_bucket/${1}/images` will replace `mys3://items/image01.jpg` with `s3://my_bucket/items/images/image01.jpg`
**📝 Note:** Replacements defined in `IMGPROXY_URL_REPLACEMENTS` are applied before `IMGPROXY_BASE_URL` is added.
## Metrics ## Metrics
### New Relic :id=new-relic-metrics ### New Relic :id=new-relic-metrics
@@ -527,7 +537,6 @@ imgproxy can send logs to syslog, but this feature is disabled by default. To en
## Miscellaneous ## Miscellaneous
* `IMGPROXY_BASE_URL`: a base URL prefix that will be added to each requested image URL. For example, if the base URL is `http://example.com/images` and `/path/to/image.png` is requested, imgproxy will download the source image from `http://example.com/images/path/to/image.png`. If the image URL already contains the prefix, it won't be added. Default: blank
* `IMGPROXY_USE_LINEAR_COLORSPACE`: when `true`, imgproxy will process images in linear colorspace. This will slow down processing. Note that images won't be fully processed in linear colorspace while shrink-on-load is enabled (see below). * `IMGPROXY_USE_LINEAR_COLORSPACE`: when `true`, imgproxy will process images in linear colorspace. This will slow down processing. Note that images won't be fully processed in linear colorspace while shrink-on-load is enabled (see below).
* `IMGPROXY_DISABLE_SHRINK_ON_LOAD`: when `true`, disables shrink-on-load for JPEGs and WebP files. Allows processing the entire image in linear colorspace but dramatically slows down resizing and increases memory usage when working with large images. * `IMGPROXY_DISABLE_SHRINK_ON_LOAD`: when `true`, disables shrink-on-load for JPEGs and WebP files. Allows processing the entire image in linear colorspace but dramatically slows down resizing and increases memory usage when working with large images.
* `IMGPROXY_STRIP_METADATA`: when `true`, imgproxy will strip all metadata (EXIF, IPTC, etc.) from JPEG and WebP output images. Default: `true` * `IMGPROXY_STRIP_METADATA`: when `true`, imgproxy will strip all metadata (EXIF, IPTC, etc.) from JPEG and WebP output images. Default: `true`

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"regexp"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -54,6 +55,20 @@ func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithBase() {
require.Equal(s.T(), imagetype.PNG, po.Format) require.Equal(s.T(), imagetype.PNG, po.Format)
} }
func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithReplacement() {
config.URLReplacements = map[*regexp.Regexp]string{
regexp.MustCompile("^test://([^/]*)/"): "http://images.dev/${1}/dolor/",
}
originURL := "test://lorem/ipsum.jpg?param=value"
path := fmt.Sprintf("/size:100:100/%s.png", base64.RawURLEncoding.EncodeToString([]byte(originURL)))
po, imageURL, err := ParsePath(path, make(http.Header))
require.Nil(s.T(), err)
require.Equal(s.T(), "http://images.dev/lorem/dolor/ipsum.jpg?param=value", imageURL)
require.Equal(s.T(), imagetype.PNG, po.Format)
}
func (s *ProcessingOptionsTestSuite) TestParsePlainURL() { func (s *ProcessingOptionsTestSuite) TestParsePlainURL() {
originURL := "http://images.dev/lorem/ipsum.jpg" originURL := "http://images.dev/lorem/ipsum.jpg"
path := fmt.Sprintf("/size:100:100/plain/%s@png", originURL) path := fmt.Sprintf("/size:100:100/plain/%s@png", originURL)
@@ -96,6 +111,20 @@ func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithBase() {
require.Equal(s.T(), imagetype.PNG, po.Format) require.Equal(s.T(), imagetype.PNG, po.Format)
} }
func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithReplacement() {
config.URLReplacements = map[*regexp.Regexp]string{
regexp.MustCompile("^test://([^/]*)/"): "http://images.dev/${1}/dolor/",
}
originURL := "test://lorem/ipsum.jpg"
path := fmt.Sprintf("/size:100:100/plain/%s@png", originURL)
po, imageURL, err := ParsePath(path, make(http.Header))
require.Nil(s.T(), err)
require.Equal(s.T(), "http://images.dev/lorem/dolor/ipsum.jpg", imageURL)
require.Equal(s.T(), imagetype.PNG, po.Format)
}
func (s *ProcessingOptionsTestSuite) TestParsePlainURLEscapedWithBase() { func (s *ProcessingOptionsTestSuite) TestParsePlainURLEscapedWithBase() {
config.BaseURL = "http://images.dev/" config.BaseURL = "http://images.dev/"

View File

@@ -12,7 +12,11 @@ import (
const urlTokenPlain = "plain" const urlTokenPlain = "plain"
func addBaseURL(u string) string { func preprocessURL(u string) string {
for re, repl := range config.URLReplacements {
u = re.ReplaceAllString(u, repl)
}
if len(config.BaseURL) == 0 || strings.HasPrefix(u, config.BaseURL) { if len(config.BaseURL) == 0 || strings.HasPrefix(u, config.BaseURL) {
return u return u
} }
@@ -43,7 +47,7 @@ func decodeBase64URL(parts []string) (string, string, error) {
return "", "", fmt.Errorf("Invalid url encoding: %s", encoded) return "", "", fmt.Errorf("Invalid url encoding: %s", encoded)
} }
return addBaseURL(string(imageURL)), format, nil return preprocessURL(string(imageURL)), format, nil
} }
func decodePlainURL(parts []string) (string, string, error) { func decodePlainURL(parts []string) (string, string, error) {
@@ -69,7 +73,7 @@ func decodePlainURL(parts []string) (string, string, error) {
return "", "", fmt.Errorf("Invalid url encoding: %s", encoded) return "", "", fmt.Errorf("Invalid url encoding: %s", encoded)
} }
return addBaseURL(unescaped), format, nil return preprocessURL(unescaped), format, nil
} }
func DecodeURL(parts []string) (string, string, error) { func DecodeURL(parts []string) (string, string, error) {