mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
Both transports reuse a RequestCtx for the next request, and a handler's context read the request's headers lazily, so a context kept past its handler answered with whichever caller the RequestCtx was serving by then. The context now holds the request through an inflight the request owns: the server closes it when it resets the request, and every read holds it while reading. A context kept past its request answers with no caller, forwards no identity, attests no peer and is not a local call. Co-authored-by: Hanzo Dev <[email protected]>
101 lines
2.9 KiB
Go
101 lines
2.9 KiB
Go
package zip
|
|
|
|
import (
|
|
"context"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
// TestDetachOutlivesTheRequest pins that a detached context answers with the
|
|
// caller its request carried after that request is finished and its RequestCtx
|
|
// is back in the server's pool, and that it no longer reaches the request.
|
|
func TestDetachOutlivesTheRequest(t *testing.T) {
|
|
app := New(Config{AppName: "svc", DisableStartupMessage: true})
|
|
type none struct{}
|
|
var (
|
|
mu sync.Mutex
|
|
det []context.Context
|
|
)
|
|
app.Get("/v1/hook", func(ctx context.Context, _ *none) (*none, error) {
|
|
mu.Lock()
|
|
det = append(det, Detach(ctx))
|
|
mu.Unlock()
|
|
return &none{}, nil
|
|
}, WithOperationID("hook"))
|
|
if err := app.Build(); err != nil {
|
|
t.Fatalf("Build: %v", err)
|
|
}
|
|
for _, org := range []string{"acme", "globex", "initech"} {
|
|
req := httptest.NewRequest("GET", "/v1/hook", nil)
|
|
req.Header.Set(HeaderOrg, org)
|
|
req.Header.Set(HeaderUser, "u-"+org)
|
|
resp, err := app.Fiber().Test(req)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", org, err)
|
|
}
|
|
_ = resp.Body.Close()
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
for i, org := range []string{"acme", "globex", "initech"} {
|
|
ctx := det[i]
|
|
if behind, _ := request(ctx, func(*fasthttp.RequestCtx) {}); behind {
|
|
t.Fatalf("%s: a detached context still reaches its request", org)
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
t.Fatalf("%s: a detached context ended with its request: %v", org, err)
|
|
}
|
|
if c := CallerOf(ctx); c.Org != org || c.User != "u-"+org {
|
|
t.Fatalf("%s: detached caller is %+v", org, c)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestAContextKeptPastItsRequestReachesNoOne pins the backstop under Detach: a
|
|
// handler's own context, kept after its request ended, answers with no caller
|
|
// and as no local call — never with the caller of the request the RequestCtx
|
|
// serves next.
|
|
func TestAContextKeptPastItsRequestReachesNoOne(t *testing.T) {
|
|
app := New(Config{AppName: "svc", DisableStartupMessage: true})
|
|
type none struct{}
|
|
var (
|
|
mu sync.Mutex
|
|
kept []context.Context
|
|
)
|
|
app.Get("/v1/keep", func(ctx context.Context, _ *none) (*none, error) {
|
|
mu.Lock()
|
|
kept = append(kept, ctx)
|
|
mu.Unlock()
|
|
return &none{}, nil
|
|
}, WithOperationID("keep"))
|
|
if err := app.Build(); err != nil {
|
|
t.Fatalf("Build: %v", err)
|
|
}
|
|
for _, org := range []string{"acme", "globex"} {
|
|
req := httptest.NewRequest("GET", "/v1/keep", nil)
|
|
req.Header.Set(HeaderOrg, org)
|
|
req.Header.Set(HeaderUser, "u-"+org)
|
|
resp, err := app.Fiber().Test(req)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", org, err)
|
|
}
|
|
_ = resp.Body.Close()
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
for i, ctx := range kept {
|
|
if c := CallerOf(ctx); c != (Caller{}) {
|
|
t.Fatalf("request %d: a context kept past its request answers as %+v", i, c)
|
|
}
|
|
if Local(ctx) {
|
|
t.Fatalf("request %d: a context kept past its request reads as a local call", i)
|
|
}
|
|
if PeerOf(ctx) != nil || Header(ctx, HeaderOrg) != "" {
|
|
t.Fatalf("request %d: a context kept past its request still reads the request", i)
|
|
}
|
|
}
|
|
}
|