feat: Add response body capture to ContentBasedETagMiddleware for accurate content-based ETag generation

- Add ResponseWriterWrapper struct with gin.ResponseWriter and body buffer fields to capture response content
- Add Write method to ResponseWriterWrapper that captures response body while writing to original writer
- Add Body method to ResponseWriterWrapper that returns captured response bytes
- Update ContentBasedETagMiddleware to wrap response writer with ResponseWriterWrapper before calling
This commit is contained in:
nutcas3
2026-04-23 02:59:18 +03:00
parent cae53ebd17
commit 5a3f08829f
@@ -1,6 +1,7 @@
package middleware package middleware
import ( import (
"bytes"
"context" "context"
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
@@ -12,6 +13,23 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// ResponseWriterWrapper wraps gin.ResponseWriter to capture response body
type ResponseWriterWrapper struct {
gin.ResponseWriter
body *bytes.Buffer
}
// Write captures the response body while writing to the original writer
func (w *ResponseWriterWrapper) Write(data []byte) (int, error) {
w.body.Write(data)
return w.ResponseWriter.Write(data)
}
// Body returns the captured response body
func (w *ResponseWriterWrapper) Body() []byte {
return w.body.Bytes()
}
// PerformanceMetrics tracks performance statistics // PerformanceMetrics tracks performance statistics
type PerformanceMetrics struct { type PerformanceMetrics struct {
mu sync.RWMutex mu sync.RWMutex
@@ -445,6 +463,13 @@ func ETagValidationMiddleware() gin.HandlerFunc {
// ContentBasedETagMiddleware generates ETags based on response content // ContentBasedETagMiddleware generates ETags based on response content
func ContentBasedETagMiddleware() gin.HandlerFunc { func ContentBasedETagMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
// Wrap the response writer to capture the body
wrapper := &ResponseWriterWrapper{
ResponseWriter: c.Writer,
body: bytes.NewBuffer(nil),
}
c.Writer = wrapper
c.Next() c.Next()
// Only generate ETags for successful GET requests // Only generate ETags for successful GET requests
@@ -452,10 +477,16 @@ func ContentBasedETagMiddleware() gin.HandlerFunc {
return return
} }
// For content-based ETags, we would need to capture the response body // Generate ETag based on captured response content
// This is a simplified version that uses weak ETags for dynamic content responseBody := wrapper.Body()
etag := generateWeakETag(c) if len(responseBody) > 0 {
c.Header("ETag", etag) etag := generateContentETag(c, responseBody)
c.Header("ETag", etag)
} else {
// Fallback to weak ETag for empty responses
etag := generateWeakETag(c)
c.Header("ETag", etag)
}
} }
} }