Bläddra i källkod

Add URL replacements

DarthSim 2 år sedan
förälder
incheckning
bc5ca9a344

+ 2 - 0
CHANGELOG.md

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

+ 6 - 1
config/config.go

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

+ 21 - 1
config/configurators/configurators.go

@@ -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 {
 	var result strings.Builder
 	// Perform prefix matching
@@ -228,7 +248,7 @@ func RegexpFromPattern(pattern string) *regexp.Regexp {
 	for i, part := range strings.Split(pattern, "*") {
 		// Add a regexp match all without slashes for each wildcard character
 		if i > 0 {
-			result.WriteString("[^/]*")
+			result.WriteString("([^/]*)")
 		}
 
 		// Quote other parts of the pattern

+ 10 - 1
docs/configuration.md

@@ -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.
 
+## 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
 
 ### 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
 
-* `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_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`

+ 29 - 0
options/processing_options_test.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"net/http"
 	"net/url"
+	"regexp"
 	"testing"
 
 	"github.com/stretchr/testify/require"
@@ -54,6 +55,20 @@ func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithBase() {
 	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() {
 	originURL := "http://images.dev/lorem/ipsum.jpg"
 	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)
 }
 
+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() {
 	config.BaseURL = "http://images.dev/"
 

+ 7 - 3
options/url.go

@@ -12,7 +12,11 @@ import (
 
 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) {
 		return u
 	}
@@ -43,7 +47,7 @@ func decodeBase64URL(parts []string) (string, string, error) {
 		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) {
@@ -69,7 +73,7 @@ func decodePlainURL(parts []string) (string, string, error) {
 		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) {