mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-09-29 21:13:57 +02:00
Refactor ETag support
This commit is contained in:
12
config.go
12
config.go
@@ -95,8 +95,9 @@ type config struct {
|
||||
Secret string
|
||||
|
||||
LocalFileSystemRoot string
|
||||
ETagEnabled bool
|
||||
RandomValue []byte
|
||||
|
||||
ETagEnabled bool
|
||||
ETagSignature []byte
|
||||
}
|
||||
|
||||
var conf = config{
|
||||
@@ -146,6 +147,7 @@ func init() {
|
||||
strEnvConfig(&conf.Secret, "IMGPROXY_SECRET")
|
||||
|
||||
strEnvConfig(&conf.LocalFileSystemRoot, "IMGPROXY_LOCAL_FILESYSTEM_ROOT")
|
||||
|
||||
boolEnvConfig(&conf.ETagEnabled, "IMGPROXY_USE_ETAG")
|
||||
|
||||
if len(conf.Key) == 0 {
|
||||
@@ -218,10 +220,10 @@ func init() {
|
||||
}
|
||||
|
||||
if conf.ETagEnabled {
|
||||
conf.RandomValue = make([]byte, 16)
|
||||
rand.Read(conf.RandomValue)
|
||||
conf.ETagSignature = make([]byte, 16)
|
||||
rand.Read(conf.ETagSignature)
|
||||
log.Printf("ETag support is activated. The random value was generated to be used for ETag calculation: %s\n",
|
||||
fmt.Sprintf("%x", conf.RandomValue))
|
||||
fmt.Sprintf("%x", conf.ETagSignature))
|
||||
}
|
||||
|
||||
initVips()
|
||||
|
27
etag.go
27
etag.go
@@ -4,38 +4,17 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// checks whether client's ETag matches current response body.
|
||||
// - if the IMGPROXY_USE_ETAG env var is unset, this function always returns false
|
||||
// - if the IMGPROXY_USE_ETAG is set to "true", the function calculates current ETag and compares it
|
||||
// with another ETag value provided by a client request
|
||||
// Note that the calculated ETag value is saved to outcoming response with "ETag" header.
|
||||
func isETagMatching(b []byte, po *processingOptions, rw *http.ResponseWriter, r *http.Request) bool {
|
||||
|
||||
if !conf.ETagEnabled {
|
||||
return false
|
||||
}
|
||||
|
||||
// calculate current ETag value using sha1 hashing function
|
||||
currentEtagValue := calculateHashSumFor(b, po)
|
||||
(*rw).Header().Set("ETag", currentEtagValue)
|
||||
return currentEtagValue == r.Header.Get("If-None-Match")
|
||||
}
|
||||
|
||||
// the function calculates the SHA checksum for the current image and current Processing Options.
|
||||
// The principal is very simple: if an original image is the same and POs are the same, then
|
||||
// the checksum must be always identical. But if PO has some different parameters, the
|
||||
// checksum must be different even if original images match
|
||||
func calculateHashSumFor(b []byte, po *processingOptions) string {
|
||||
var notModifiedErr = newError(304, "Not modified", "Not modified")
|
||||
|
||||
func calcETag(b []byte, po *processingOptions) string {
|
||||
footprint := sha1.Sum(b)
|
||||
|
||||
hash := sha1.New()
|
||||
hash.Write(footprint[:])
|
||||
binary.Write(hash, binary.LittleEndian, *po)
|
||||
hash.Write(conf.RandomValue)
|
||||
hash.Write(conf.ETagSignature)
|
||||
|
||||
return fmt.Sprintf("%x", hash.Sum(nil))
|
||||
}
|
||||
|
15
server.go
15
server.go
@@ -109,7 +109,6 @@ func respondWithImage(r *http.Request, rw http.ResponseWriter, data []byte, imgU
|
||||
rw.Header().Set("Expires", time.Now().Add(time.Second*time.Duration(conf.TTL)).Format(http.TimeFormat))
|
||||
rw.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d, public", conf.TTL))
|
||||
rw.Header().Set("Content-Type", mimes[po.Format])
|
||||
rw.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))
|
||||
|
||||
if gzipped {
|
||||
rw.Header().Set("Content-Encoding", "gzip")
|
||||
@@ -199,13 +198,17 @@ func (h *httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
t.Check()
|
||||
|
||||
if isETagMatching(b, &procOpt, &rw, r) {
|
||||
// if client has its own locally cached copy of this file, then return 304, no need to send it again over the network
|
||||
rw.WriteHeader(304)
|
||||
logResponse(304, fmt.Sprintf("Returned 'Not Modified' instead of actual image in %s: %s; %+v", t.Since(), imgURL, procOpt))
|
||||
return
|
||||
if conf.ETagEnabled {
|
||||
eTag := calcETag(b, &procOpt)
|
||||
rw.Header().Set("ETag", eTag)
|
||||
|
||||
if eTag == r.Header.Get("If-None-Match") {
|
||||
panic(notModifiedErr)
|
||||
}
|
||||
}
|
||||
|
||||
t.Check()
|
||||
|
||||
b, err = processImage(b, imgtype, procOpt, t)
|
||||
if err != nil {
|
||||
panic(newError(500, err.Error(), "Error occurred while processing image"))
|
||||
|
Reference in New Issue
Block a user