Skip to content

Commit

Permalink
fix(rfc): Add support for Vary star (#435)
Browse files Browse the repository at this point in the history
Co-authored-by: Vincent Jordan <[email protected]>
  • Loading branch information
vejipe and Vincent Jordan authored Jan 9, 2024
1 parent 7fb48f5 commit 85a1b1e
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 43 deletions.
85 changes: 47 additions & 38 deletions pkg/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,43 +234,50 @@ func (s *SouinBaseHandler) Store(
res.Header.Set(rfc.StoredLengthHeader, res.Header.Get("Content-Length"))
response, err := httputil.DumpResponse(&res, true)
if err == nil && customWriter.Buf.Len() > 0 {
variedHeaders := rfc.HeaderAllCommaSepValues(res.Header)
cachedKey += rfc.GetVariedCacheKey(rq, variedHeaders)
s.Configuration.GetLogger().Sugar().Debugf("Store the response %+v with duration %v", res, ma)

var wg sync.WaitGroup
mu := sync.Mutex{}
fails := []string{}
select {
case <-rq.Context().Done():
status += "; detail=REQUEST-CANCELED-OR-UPSTREAM-BROKEN-PIPE"
default:
for _, storer := range s.Storers {
wg.Add(1)
go func(currentStorer storage.Storer) {
defer wg.Done()
if currentStorer.Set(cachedKey, response, currentMatchedURL, ma) == nil {
s.Configuration.GetLogger().Sugar().Debugf("Stored the key %s in the %s provider", cachedKey, currentStorer.Name())
} else {
mu.Lock()
fails = append(fails, fmt.Sprintf("; detail=%s-INSERTION-ERROR", currentStorer.Name()))
mu.Unlock()
}
}(storer)
}
variedHeaders, isVaryStar := rfc.VariedHeaderAllCommaSepValues(res.Header)
if isVaryStar {
// "Implies that the response is uncacheable"
status += "; detail=UPSTREAM-VARY-STAR"
} else {
cachedKey += rfc.GetVariedCacheKey(rq, variedHeaders)

s.Configuration.GetLogger().Sugar().Debugf("Store the response %+v with duration %v", res, ma)

var wg sync.WaitGroup
mu := sync.Mutex{}
fails := []string{}
select {
case <-rq.Context().Done():
status += "; detail=REQUEST-CANCELED-OR-UPSTREAM-BROKEN-PIPE"
default:
for _, storer := range s.Storers {
wg.Add(1)
go func(currentStorer storage.Storer) {
defer wg.Done()
if currentStorer.Set(cachedKey, response, currentMatchedURL, ma) == nil {
s.Configuration.GetLogger().Sugar().Debugf("Stored the key %s in the %s provider", cachedKey, currentStorer.Name())
} else {
mu.Lock()
fails = append(fails, fmt.Sprintf("; detail=%s-INSERTION-ERROR", currentStorer.Name()))
mu.Unlock()
}
}(storer)
}

wg.Wait()
if len(fails) < s.storersLen {
go func(rs http.Response, key string) {
_ = s.SurrogateKeyStorer.Store(&rs, key)
}(res, cachedKey)
status += "; stored"
}
wg.Wait()
if len(fails) < s.storersLen {
go func(rs http.Response, key string) {
_ = s.SurrogateKeyStorer.Store(&rs, key)
}(res, cachedKey)
status += "; stored"
}

if len(fails) > 0 {
status += strings.Join(fails, "")
if len(fails) > 0 {
status += strings.Join(fails, "")
}
}
}

} else {
status += "; detail=UPSTREAM-ERROR-OR-EMPTY-RESPONSE"
}
Expand Down Expand Up @@ -337,11 +344,13 @@ func (s *SouinBaseHandler) Upstream(

if sfWriter, ok := sfValue.(singleflightValue); ok && shared {
if vary := sfWriter.headers.Get("Vary"); vary != "" {
variedHeaders := rfc.HeaderAllCommaSepValues(sfWriter.headers)
for _, vh := range variedHeaders {
if rq.Header.Get(vh) != sfWriter.requestHeaders.Get(vh) {
cachedKey += rfc.GetVariedCacheKey(rq, variedHeaders)
return s.Upstream(customWriter, rq, next, requestCc, cachedKey)
variedHeaders, isVaryStar := rfc.VariedHeaderAllCommaSepValues(sfWriter.headers)
if !isVaryStar {
for _, vh := range variedHeaders {
if rq.Header.Get(vh) != sfWriter.requestHeaders.Get(vh) {
cachedKey += rfc.GetVariedCacheKey(rq, variedHeaders)
return s.Upstream(customWriter, rq, next, requestCc, cachedKey)
}
}
}
}
Expand Down
14 changes: 9 additions & 5 deletions pkg/rfc/vary.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,24 @@ func GetVariedCacheKey(rq *http.Request, headers []string) string {
return VarySeparator + strings.Join(headers, DecodedHeaderSeparator)
}

// headerAllCommaSepValues returns all comma-separated values (each
// with whitespace trimmed) for header name in headers. According to
// VariedHeaderAllCommaSepValues returns all comma-separated values (each
// with whitespace trimmed) for header name in 'Vary'. According to
// Section 4.2 of the HTTP/1.1 spec
// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2),
// values from multiple occurrences of a header should be concatenated, if
// the header's value is a comma-separated list.
func HeaderAllCommaSepValues(headers http.Header) []string {
func VariedHeaderAllCommaSepValues(headers http.Header) ([]string, bool) {
var vals []string
for _, val := range headers[http.CanonicalHeaderKey("Vary")] {
fields := strings.Split(val, ",")
for i, f := range fields {
fields[i] = strings.TrimSpace(f)
trimmedField := strings.TrimSpace(f)
if trimmedField == "*" {
return []string{"*"}, true
}
fields[i] = trimmedField
}
vals = append(vals, fields...)
}
return vals
return vals, false
}

0 comments on commit 85a1b1e

Please sign in to comment.