diff --git a/backend/app/rest/proxy/image.go b/backend/app/rest/proxy/image.go index 16abf9d0..8284bda9 100644 --- a/backend/app/rest/proxy/image.go +++ b/backend/app/rest/proxy/image.go @@ -3,13 +3,10 @@ package proxy import ( "bytes" "context" - "crypto/sha1" //nolint:gosec // not used for cryptography "encoding/base64" - "fmt" "io" "io/ioutil" "net/http" - "net/url" "strings" "time" @@ -92,7 +89,7 @@ func (p Image) Handler(w http.ResponseWriter, r *http.Request) { imgURL := string(src) var img []byte - imgID, err := cachedImgID(imgURL) + imgID, err := image.CachedImgID(imgURL) if err != nil { rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't parse image url "+imgURL, rest.ErrAssetNotFound) return @@ -174,20 +171,3 @@ func (p Image) downloadImage(ctx context.Context, imgURL string) ([]byte, error) } return imgData, nil } - -func sha1Str(s string) string { - return fmt.Sprintf("%x", sha1.Sum([]byte(s))) //nolint:gosec // not used for cryptography -} - -// generates ID for a cached image. -// ID would look like: "cached_images/-" -// - would allow us to identify all images from particular site if ever needed -// - would allow us to avoid storing duplicates of the same image -// (as accurate as deduplication based on potentially mutable url can be) -func cachedImgID(imgURL string) (string, error) { - parsedURL, err := url.Parse(imgURL) - if err != nil { - return "", errors.Wrapf(err, "can parse url %s", imgURL) - } - return fmt.Sprintf("cached_images/%s-%s", sha1Str(parsedURL.Hostname()), sha1Str(imgURL)), nil -} diff --git a/backend/app/rest/proxy/image_test.go b/backend/app/rest/proxy/image_test.go index 6eb84f06..398b38f8 100644 --- a/backend/app/rest/proxy/image_test.go +++ b/backend/app/rest/proxy/image_test.go @@ -186,7 +186,7 @@ func TestImage_RoutesCachingImage(t *testing.T) { assert.Equal(t, "image/png", resp.Header["Content-Type"][0]) imageStore.AssertCalled(t, "Load", mock.Anything) - imageStore.AssertCalled(t, "Save", "cached_images/4b84b15bff6ee5796152495a230e45e3d7e947d9-"+sha1Str(imgURL), gopherPNGBytes()) + imageStore.AssertCalled(t, "Save", "cached_images/4b84b15bff6ee5796152495a230e45e3d7e947d9-"+image.Sha1Str(imgURL), gopherPNGBytes()) imageStore.AssertCalled(t, "Commit", mock.Anything) } diff --git a/backend/app/store/image/image.go b/backend/app/store/image/image.go index 2f5cc7b9..7289b506 100644 --- a/backend/app/store/image/image.go +++ b/backend/app/store/image/image.go @@ -8,6 +8,8 @@ package image import ( "bytes" "context" + "crypto/sha1" //nolint:gosec // not used for cryptography + "fmt" "image" // support gif and jpeg images decoding _ "image/gif" @@ -16,6 +18,7 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "path" "strings" "sync" @@ -323,3 +326,21 @@ func readAndValidateImage(r io.Reader, maxSize int) ([]byte, error) { func guid() string { return xid.New().String() } + +// Sha1Str converts provided string to sha1 +func Sha1Str(s string) string { + return fmt.Sprintf("%x", sha1.Sum([]byte(s))) //nolint:gosec // not used for cryptography +} + +// CachedImgID generates ID for a cached image. +// ID would look like: "cached_images/-" +// - would allow us to identify all images from particular site if ever needed +// - would allow us to avoid storing duplicates of the same image +// (as accurate as deduplication based on potentially mutable url can be) +func CachedImgID(imgURL string) (string, error) { + parsedURL, err := url.Parse(imgURL) + if err != nil { + return "", errors.Wrapf(err, "can parse url %s", imgURL) + } + return fmt.Sprintf("cached_images/%s-%s", Sha1Str(parsedURL.Hostname()), Sha1Str(imgURL)), nil +} diff --git a/backend/app/store/image/image_test.go b/backend/app/store/image/image_test.go index cd77995f..259cc8c8 100644 --- a/backend/app/store/image/image_test.go +++ b/backend/app/store/image/image_test.go @@ -217,3 +217,13 @@ func TestGetProportionalSizes(t *testing.T) { }) } } + +func TestCachedImgID(t *testing.T) { + img, err := CachedImgID(" http://foo.com") + assert.Error(t, err) + assert.Empty(t, img) + imgURL := "http://example.org/img/1.png" + img, err = CachedImgID(imgURL) + assert.NoError(t, err) + assert.Equal(t, "cached_images/"+Sha1Str("example.org")+"-"+Sha1Str(imgURL), img) +}