Skip to content

Commit b4aa0b5

Browse files
committed
handle ETag + If-None-Match
1 parent e871430 commit b4aa0b5

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

headers.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package CADDY_FILE_SERVER
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"github.com/cespare/xxhash/v2"
7+
"net/url"
8+
"regexp"
9+
"sort"
10+
)
11+
12+
func getProcessedImageEtag(initialEtag string, form *url.Values) string {
13+
// Return early if the initial ETag is empty
14+
if initialEtag == "" {
15+
return ""
16+
}
17+
18+
re := regexp.MustCompile(`^(W/"|")(.*?)(")$`)
19+
matches := re.FindStringSubmatch(initialEtag)
20+
if len(matches) != 4 {
21+
return initialEtag
22+
}
23+
24+
// Create a slice to store the sorted parameters
25+
var params []string
26+
for key, values := range *form {
27+
// Add only the first value for each key (to avoid duplicates)
28+
params = append(params, key+"="+values[0])
29+
}
30+
31+
// Sort the parameters to ensure consistent order
32+
sort.Strings(params)
33+
34+
// Use a bytes.Buffer to join parameters efficiently
35+
var buffer bytes.Buffer
36+
for _, param := range params {
37+
if buffer.Len() > 0 {
38+
buffer.WriteString("&")
39+
}
40+
buffer.WriteString(param)
41+
}
42+
43+
// Generate the SHA1 hash of the concatenated parameters
44+
hash := xxhash.New()
45+
_, err := hash.Write(buffer.Bytes())
46+
if err != nil {
47+
return ""
48+
}
49+
hashString := fmt.Sprintf("%x", hash.Sum(nil))
50+
return matches[1] + matches[2] + "-" + hashString + matches[3]
51+
}

module.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@ func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next cadd
138138
}
139139
}
140140

141+
// Generate specific ETag ig necessary
142+
processedEtag := getProcessedImageEtag(responseRecorder.Header().Get("ETag"), &r.Form)
143+
if processedEtag != "" {
144+
responseRecorder.Header().Del("ETag") // Remove initial ETag
145+
w.Header().Set("ETag", processedEtag)
146+
147+
// Check If-None-Match header to avoid reprocessing
148+
ifNoneMatchHeader := r.Header.Get("If-None-Match")
149+
if ifNoneMatchHeader != "" && ifNoneMatchHeader == processedEtag {
150+
w.WriteHeader(http.StatusNotModified)
151+
return nil
152+
}
153+
}
154+
141155
// Parse options
142156
options, err := getOptions(&r.Form)
143157
if err != nil {
@@ -164,7 +178,7 @@ func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next cadd
164178
w.Header().Del("Content-Length")
165179
w.Header().Del("Content-Encoding")
166180
w.Header().Del("Vary")
167-
w.Header().Del("ETag")
181+
//w.Header().Del("ETag")
168182

169183
// Set new headers
170184
w.Header().Set("Content-Length", strconv.Itoa(len(newImage)))

0 commit comments

Comments
 (0)