enhance: support ETag on streamed repository archives, support If-None-Match: * (#39289)

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
silverwind
2026-09-16 12:30:22 +02:00
committed by GitHub
parent 31b4d79a84
commit c04802b6b3
4 changed files with 65 additions and 9 deletions
+14 -7
View File
@@ -55,13 +55,20 @@ func CacheControlForPrivateStatic() *CacheControlOptions {
// checkIfNoneMatchIsValid tests if the header If-None-Match matches the ETag
func checkIfNoneMatchIsValid(req *http.Request, etag string) bool {
ifNoneMatch := req.Header.Get("If-None-Match")
if len(ifNoneMatch) > 0 {
for item := range strings.SplitSeq(ifNoneMatch, ",") {
item = strings.TrimPrefix(strings.TrimSpace(item), "W/") // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives
if item == etag {
return true
}
ifNoneMatch := strings.TrimSpace(req.Header.Get("If-None-Match"))
if ifNoneMatch == "" {
return false
}
// https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2
if ifNoneMatch == "*" {
return true
}
// https://www.rfc-editor.org/rfc/rfc9110#section-8.8.3.2
etag = strings.TrimPrefix(etag, "W/")
for item := range strings.SplitSeq(ifNoneMatch, ",") {
item = strings.TrimPrefix(strings.TrimSpace(item), "W/")
if item == etag {
return true
}
}
return false
+26 -1
View File
@@ -16,11 +16,13 @@ import (
func TestHandleGenericETagCache(t *testing.T) {
matchedEtag := `"matched-etag"`
weakEtag := `W/"matched-etag"`
lastModifiedTime := new(time.Date(2021, time.January, 2, 15, 4, 5, 0, time.FixedZone("test-zone", 8*3600)))
lastModified := lastModifiedTime.UTC().Format(http.TimeFormat)
cacheControl := "max-age=0, private, must-revalidate"
type testCase struct {
name string
respEtag string
reqHeaders map[string]string
wantHandled bool
wantHeaders map[string]string
@@ -51,6 +53,29 @@ func TestHandleGenericETagCache(t *testing.T) {
wantHandled: false,
wantHeaders: map[string]string{"Last-Modified": lastModified, "Cache-Control": cacheControl, "Etag": matchedEtag},
},
{
name: "Wildcard If-None-Match matches any current representation",
reqHeaders: map[string]string{"If-None-Match": "*"},
wantHandled: true,
wantHeaders: map[string]string{"Last-Modified": lastModified, "Cache-Control": "", "Etag": matchedEtag},
wantStatus: http.StatusNotModified,
},
{
name: "Weak response tag matched by a strong request tag",
respEtag: weakEtag,
reqHeaders: map[string]string{"If-None-Match": matchedEtag},
wantHandled: true,
wantHeaders: map[string]string{"Last-Modified": lastModified, "Cache-Control": "", "Etag": weakEtag},
wantStatus: http.StatusNotModified,
},
{
name: "Weak response tag matched by a weak request tag",
respEtag: weakEtag,
reqHeaders: map[string]string{"If-None-Match": weakEtag},
wantHandled: true,
wantHeaders: map[string]string{"Last-Modified": lastModified, "Cache-Control": "", "Etag": weakEtag},
wantStatus: http.StatusNotModified,
},
{
name: "Multiple Matched If-None-Match",
reqHeaders: map[string]string{"If-None-Match": `"mismatched-etag", ` + matchedEtag},
@@ -67,7 +92,7 @@ func TestHandleGenericETagCache(t *testing.T) {
req.Header.Set(k, v)
}
w := httptest.NewRecorder()
assert.Equal(t, tc.wantHandled, HandleGenericETagPrivateCache(req, w, matchedEtag, lastModifiedTime))
assert.Equal(t, tc.wantHandled, HandleGenericETagPrivateCache(req, w, util.IfZero(tc.respEtag, matchedEtag), lastModifiedTime))
resp := w.Result()
for k, v := range tc.wantHeaders {
assert.Equal(t, v, resp.Header.Get(k))