浏览代码

Refactor ETag support

DarthSim 7 年之前
父节点
当前提交
66cea81602
共有 3 个文件被更改,包括 19 次插入35 次删除
  1. 7 5
      config.go
  2. 3 24
      etag.go
  3. 9 6
      server.go

+ 7 - 5
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()

+ 3 - 24
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))
 }

+ 9 - 6
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"))