1
Fork 0

Implement precondition checking for objects with no ETag.

This commit is contained in:
Juliusz Chroboczek 2024-04-11 13:09:05 +02:00
parent a3f894a31e
commit e20a3a829e
2 changed files with 95 additions and 0 deletions

View File

@ -70,6 +70,34 @@ func etagMatch(etag, header string) bool {
return false return false
} }
// etagMatchNoEtag evaluates preconditions for an object with no ETag.
func etagMatchNoEtag(exists bool, header string) bool {
if header == "" {
return false
}
for {
header = strings.TrimLeft(header, " \t\n\r")
if len(header) == 0 {
break
}
if header[0] == ',' {
header = header[1:]
continue
}
if header[0] == '*' {
return exists
}
e, remain := scanETag(header)
if e == "" {
break
}
header = remain
}
return false
}
func writeNotModified(w http.ResponseWriter) { func writeNotModified(w http.ResponseWriter) {
// RFC 7232 section 4.1: // RFC 7232 section 4.1:
// a sender SHOULD NOT generate representation metadata other than the // a sender SHOULD NOT generate representation metadata other than the
@ -108,3 +136,25 @@ func checkPreconditions(w http.ResponseWriter, r *http.Request, etag string) (do
return false return false
} }
// checkPreconditions evaluates request preconditions for an object with no ETag.
// exists indicates whether the object exists.
func checkPreconditionsNoEtag(w http.ResponseWriter, r *http.Request, exists bool) (done bool) {
im := r.Header.Get("If-Match")
if im != "" && !etagMatchNoEtag(exists, im) {
w.WriteHeader(http.StatusPreconditionFailed)
return true
}
inm := r.Header.Get("If-None-Match")
if inm != "" && etagMatchNoEtag(exists, inm) {
if r.Method == "GET" || r.Method == "HEAD" {
writeNotModified(w)
return true
} else {
w.WriteHeader(http.StatusPreconditionFailed)
return true
}
}
return false
}

View File

@ -89,6 +89,8 @@ func TestCheckPreconditions(t *testing.T) {
{"POST", `"123"`, ``, `"123"`, 412}, {"POST", `"123"`, ``, `"123"`, 412},
{"GET", `"123"`, ``, `"124"`, 0}, {"GET", `"123"`, ``, `"124"`, 0},
{"GET", `"123"`, ``, `*`, 304}, {"GET", `"123"`, ``, `*`, 304},
{"POST", `"123"`, ``, `"124"`, 0},
{"POST", `"123"`, ``, `*`, 412},
} }
for _, tst := range tests { for _, tst := range tests {
@ -112,3 +114,46 @@ func TestCheckPreconditions(t *testing.T) {
} }
} }
} }
func TestCheckPreconditionsNoEtag(t *testing.T) {
var tests = []struct {
method string
exists bool
im, inm string
result int
}{
{"GET", false, ``, ``, 0},
{"GET", false, `*`, ``, 412},
{"GET", false, ``, `*`, 0},
{"POST", false, `*`, ``, 412},
{"POST", false, ``, `*`, 0},
{"GET", true, ``, ``, 0},
{"GET", true, `"124"`, ``, 412},
{"POST", true, `"124"`, ``, 412},
{"GET", true, `*`, ``, 0},
{"GET", true, ``, `*`, 304},
{"POST", true, `*`, ``, 0},
{"POST", true, ``, `*`, 412},
}
for _, tst := range tests {
var w testWriter
h := make(http.Header)
if tst.im != "" {
h.Set("If-Match", tst.im)
}
if tst.inm != "" {
h.Set("If-None-Match", tst.inm)
}
r := http.Request{
Method: tst.method,
Header: h,
}
done := checkPreconditionsNoEtag(&w, &r, tst.exists)
if done != (tst.result != 0) || w.statusCode != tst.result {
t.Errorf("%v %#v %#v: got %v, expected %v",
tst.exists, tst.im, tst.inm,
w.statusCode, tst.result)
}
}
}