mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-11-07 08:34:16 +00:00
fix(coalesce): copy the primary response body for waiters
Waiters read the primary response buffer while fasthttp could reuse it for another request. A waiter could then send the wrong body. The primary now gives waiters their own copy.
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Pins theorems/Theorems/CoalesceAlias.lean: waiters never alias the primary's response buffer.
|
||||
func TestSnapshotCoalescedResponse_OwnsBody(t *testing.T) {
|
||||
app := fiber.New()
|
||||
fctx := &fasthttp.RequestCtx{}
|
||||
c := app.AcquireCtx(fctx)
|
||||
defer app.ReleaseCtx(c)
|
||||
|
||||
c.Response().SetStatusCode(201)
|
||||
c.Response().SetBody([]byte(`{"user":"alice"}`))
|
||||
|
||||
snap := snapshotCoalescedResponse(c)
|
||||
|
||||
c.Response().SetBody([]byte(`{"user":"bobby"}`)) // buffer reused by a later request
|
||||
|
||||
assert.Equal(t, 201, snap.StatusCode)
|
||||
assert.Equal(t, `{"user":"alice"}`, string(snap.Body))
|
||||
}
|
||||
@@ -740,11 +740,7 @@ func performProxyRequest(c fiber.Ctx, proxyURL string) error {
|
||||
}, proxyErr
|
||||
}
|
||||
|
||||
return &CoalescedResponse{
|
||||
Body: c.Response().Body(),
|
||||
StatusCode: c.Response().StatusCode(),
|
||||
// Headers intentionally left nil; not populated or read anywhere.
|
||||
}, nil
|
||||
return snapshotCoalescedResponse(c), nil
|
||||
})
|
||||
|
||||
// Check for error from rc.Do (though it typically returns nil)
|
||||
@@ -773,6 +769,17 @@ func performProxyRequest(c fiber.Ctx, proxyURL string) error {
|
||||
return performProxyRequestCore(c, proxyURL, cacheKey)
|
||||
}
|
||||
|
||||
// snapshotCoalescedResponse captures the primary's response for waiters. It
|
||||
// copies the body because fasthttp reuses the buffer once the primary handler
|
||||
// returns, while waiters can still be reading.
|
||||
func snapshotCoalescedResponse(c fiber.Ctx) *CoalescedResponse {
|
||||
return &CoalescedResponse{
|
||||
Body: bytes.Clone(c.Response().Body()),
|
||||
StatusCode: c.Response().StatusCode(),
|
||||
// Headers intentionally left nil; not populated or read anywhere.
|
||||
}
|
||||
}
|
||||
|
||||
// performProxyRequestCore executes the proxy request with retries and circuit breaker
|
||||
// This is the core implementation used by both direct calls and coalesced requests
|
||||
func performProxyRequestCore(c fiber.Ctx, proxyURL string, cacheKey string) error {
|
||||
|
||||
Reference in New Issue
Block a user