github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/pkg/utils/etag.go (about) 1 package utils 2 3 import ( 4 "net/http" 5 "net/textproto" 6 "strings" 7 ) 8 9 // CheckPreconditions evaluates request preconditions based only on the Etag 10 // values. 11 func CheckPreconditions(w http.ResponseWriter, r *http.Request, etag string) (done bool) { 12 inm := r.Header.Get("If-None-Match") 13 14 if inm != "" && etag != "" && checkIfNoneMatch(inm, etag) { 15 h := w.Header() 16 delete(h, "Content-Type") 17 delete(h, "Content-Length") 18 w.WriteHeader(http.StatusNotModified) 19 return true 20 } 21 22 return false 23 } 24 25 func checkIfNoneMatch(ifNoneMatch, definedETag string) (match bool) { 26 buf := ifNoneMatch 27 for { 28 buf = textproto.TrimString(buf) 29 if len(buf) == 0 { 30 break 31 } 32 if buf[0] == ',' { 33 buf = buf[1:] 34 } 35 if buf[0] == '*' { 36 return true 37 } 38 etag, remain := scanETag(buf) 39 if etag == "" { 40 break 41 } 42 if etagWeakMatch(etag, definedETag) { 43 return true 44 } 45 buf = remain 46 } 47 return false 48 } 49 50 // etagWeakMatch reports whether a and b match using weak ETag comparison. 51 // Assumes a and b are valid ETags. 52 // More at: https://www.rfc-editor.org/rfc/rfc9110#name-comparison-2 53 func etagWeakMatch(a, b string) bool { 54 return strings.TrimPrefix(a, "W/") == strings.TrimPrefix(b, "W/") 55 } 56 57 // scanETag determines if a syntactically valid ETag is present at s. If so, 58 // the ETag and remaining text after consuming ETag is returned. Otherwise, 59 // it returns "", "". 60 func scanETag(s string) (etag string, remain string) { 61 start := 0 62 if len(s) >= 2 && s[0] == 'W' && s[1] == '/' { 63 start = 2 64 } 65 if len(s[start:]) < 2 || s[start] != '"' { 66 return "", "" 67 } 68 // ETag is either W/"text" or "text". 69 // See RFC 7232 2.3. 70 for i := start + 1; i < len(s); i++ { 71 c := s[i] 72 switch { 73 // Character values allowed in ETags. 74 case c == 0x21 || c >= 0x23 && c <= 0x7E || c >= 0x80: 75 case c == '"': 76 return s[:i+1], s[i+1:] 77 default: 78 return "", "" 79 } 80 } 81 return "", "" 82 }