Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions handler/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,14 @@ func fetchRemoteImg(url string, subdir string) (metaContent config.MetaFile) {
// remote file has changed
log.Info("Remote file changed, updating metadata and fetching image source...")
helper.DeleteMetadata(url, subdir)
helper.WriteMetadata(url, etag, subdir)
metadata = helper.WriteMetadata(url, etag, subdir)
} else {
// local file not exists
log.Info("Remote file not found in remote-raw, re-fetching...")
}
_ = downloadFile(localRawImagePath, url)
// Update metadata with newly downloaded file
helper.WriteMetadata(url, etag, subdir)
metadata = helper.WriteMetadata(url, etag, subdir)
}
return metadata
}
Expand Down
13 changes: 12 additions & 1 deletion handler/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,12 @@ func Convert(c *fiber.Ctx) error {
// this is proxyMode, we'll have to use this url to download and save it to local path, which also gives us rawImageAbs
// https://test.webp.sh/mypic/123.jpg?someother=200&somebugs=200

helper.SetMetedataProxyMode(true)
metadata = fetchRemoteImg(realRemoteAddr, targetHostName)
rawImageAbs = path.Join(config.Config.RemoteRawPath, targetHostName, metadata.Id) + path.Ext(realRemoteAddr)
} else {
// not proxyMode, we'll use local path
helper.SetMetedataProxyMode(false)
metadata = helper.ReadMetadata(reqURIwithQuery, "", targetHostName)
if !mapMode {
// by default images are hosted in ImgPath
Expand All @@ -150,7 +152,7 @@ func Convert(c *fiber.Ctx) error {
// detect if source file has changed
if metadata.Checksum != helper.HashFile(rawImageAbs) {
log.Info("Source file has changed, re-encoding...")
helper.WriteMetadata(reqURIwithQuery, "", targetHostName)
metadata = helper.WriteMetadata(reqURIwithQuery, "", targetHostName)
cleanProxyCache(path.Join(config.Config.ExhaustPath, targetHostName, metadata.Id))
}
}
Expand All @@ -168,6 +170,15 @@ func Convert(c *fiber.Ctx) error {
})
}

if config.Config.EnableExtraParams && helper.HasResizeParams(extraParams) {
if err := helper.ValidateNoUpscale(metadata.ImageMeta, extraParams); err != nil {
log.Warnf("Blocked resize request for %s: %v", reqURIwithQuery, err)
c.Status(http.StatusBadRequest)
_ = c.SendString(err.Error())
return nil
}
}

supportedFormats := helper.GuessSupportedFormat(reqHeader)
// resize itself and return if only raw(jpg,jpeg,png,gif) is supported
if supportedFormats["jpg"] == true &&
Expand Down
22 changes: 22 additions & 0 deletions helper/helper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package helper

import (
"errors"
"fmt"
"os"
"path"
Expand All @@ -25,6 +26,7 @@ import (
var (
boolFalse vips.BoolParameter
intMinusOne vips.IntParameter
ErrUpscaleNotAllowed = errors.New("requested resize exceeds source dimensions")
)

var _ = filetype.AddMatcher(filetype.NewType("svg", "image/svg+xml"), svgMatcher)
Expand Down Expand Up @@ -229,3 +231,23 @@ func HashFile(filepath string) string {
buf, _ := os.ReadFile(filepath)
return fmt.Sprintf("%x", xxhash.Sum64(buf))
}

func HasResizeParams(extra config.ExtraParams) bool {
return extra.Width > 0 || extra.Height > 0 || extra.MaxWidth > 0 || extra.MaxHeight > 0
}

func ValidateNoUpscale(meta config.ImageMeta, extra config.ExtraParams) error {
if !HasResizeParams(extra) || meta.Width == 0 || meta.Height == 0 {
return nil
}

if extra.Width > 0 && extra.Width > meta.Width {
return fmt.Errorf("%w: requested width %d exceeds source width %d", ErrUpscaleNotAllowed, extra.Width, meta.Width)
}

if extra.Height > 0 && extra.Height > meta.Height {
return fmt.Errorf("%w: requested height %d exceeds source height %d", ErrUpscaleNotAllowed, extra.Height, meta.Height)
}

return nil
}
76 changes: 76 additions & 0 deletions helper/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,79 @@ func TestGuessSupportedFormat(t *testing.T) {
})
}
}

func TestHasResizeParams(t *testing.T) {
if HasResizeParams(config.ExtraParams{}) {
t.Fatalf("expected empty params to report no resize request")
}

params := config.ExtraParams{Width: 100}
if !HasResizeParams(params) {
t.Fatalf("expected Width to trigger resize detection")
}

params = config.ExtraParams{MaxHeight: 80}
if !HasResizeParams(params) {
t.Fatalf("expected MaxHeight to trigger resize detection")
}
}

func TestValidateNoUpscale(t *testing.T) {
meta := config.ImageMeta{Width: 200, Height: 100}

tests := []struct {
params config.ExtraParams
wantError bool
}{
// Tests for no resize params
{
params: config.ExtraParams{},
wantError: false,
},
// Tests for shrink Width only
{
params: config.ExtraParams{Width: 150},
wantError: false,
},
// Tests for shrink Height only
{
params: config.ExtraParams{Height: 80},
wantError: false,
},
// Tests for shrink both Width and Height
{
params: config.ExtraParams{Width: 150, Height: 80},
wantError: false,
},
// Tests for upscale Width only
{
params: config.ExtraParams{Width: 250},
wantError: true,
},
// Tests for upscale Height only
{
params: config.ExtraParams{Height: 150},
wantError: true,
},
// Tests for upscale both Width and Height
{
params: config.ExtraParams{Width: 300, Height: 200},
wantError: true,
},
// Tests for MaxWidth and MaxHeight
{
params: config.ExtraParams{MaxWidth: 300, MaxHeight: 400},
wantError: false,
},
}

for _, tc := range tests {
err := ValidateNoUpscale(meta, tc.params)
if tc.wantError && err == nil {
t.Fatalf("TestValidateNoUpscale failed with error %s", err)
}
if !tc.wantError && err != nil {
t.Fatalf("TestValidateNoUpscale failed with error %s", err)
}
}
}
14 changes: 12 additions & 2 deletions helper/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ import (
log "github.com/sirupsen/logrus"
)

var (
ProxyMode = false
)
// Get ID and filepath
// For ProxyMode, pass in p the remote-raw path

func getId(p string, subdir string) (id string, filePath string, santizedPath string) {
if config.ProxyMode {
if ProxyMode {
fileID := HashString(p)
return fileID, path.Join(config.Config.RemoteRawPath, subdir, fileID) + path.Ext(p), ""
}
Expand Down Expand Up @@ -71,7 +75,9 @@ func WriteMetadata(p, etag string, subdir string) config.MetaFile {
}

// Only get image metadata if the file has image extension
if CheckImageExtension(filepath) {
// extract path from URL
parsedURL, _ := url.Parse(filepath)
if CheckImageExtension(parsedURL.Path) {
imageMeta := getImageMeta(filepath)
data.ImageMeta = imageMeta
}
Expand Down Expand Up @@ -179,3 +185,7 @@ func DeleteMetadata(p string, subdir string) {
log.Warnln("failed to delete metadata", err)
}
}

func SetMetedataProxyMode(proxy bool) {
ProxyMode = proxy
}
4 changes: 2 additions & 2 deletions helper/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestGetId(t *testing.T) {

t.Run("proxy mode", func(t *testing.T) {
// Test case 1: Proxy mode
config.ProxyMode = true
SetMetedataProxyMode(true)
id, jointPath, santizedPath := getId(p, "")

// Verify the return values
Expand All @@ -26,7 +26,7 @@ func TestGetId(t *testing.T) {
})
t.Run("non-proxy mode", func(t *testing.T) {
// Test case 2: Non-proxy mode
config.ProxyMode = false
SetMetedataProxyMode(false)
p = "/image.jpg?width=400&height=500"
id, jointPath, santizedPath := getId(p, "")

Expand Down
Loading