core: preserve metrics registry in Context.WithValue (#7861)

* core: preserve metrics registry in Context.WithValue

Context.WithValue rebuilt the Context without copying the unexported
metricsRegistry field, so any module provisioned under a context
derived via WithValue saw a nil registry from GetMetricsRegistry().

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Signed-off-by: Kévin Dunglas <kevin@dunglas.fr>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
pull/7853/head^2
Kévin Dunglas 2026-07-07 20:52:36 +02:00 committed by GitHub
parent 08ad064160
commit 4e62095245
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 19 additions and 0 deletions

View File

@ -676,6 +676,7 @@ func (ctx *Context) WithValue(key, value any) Context {
ancestry: ctx.ancestry,
cleanupFuncs: ctx.cleanupFuncs,
exitFuncs: ctx.exitFuncs,
metricsRegistry: ctx.metricsRegistry,
}
}

View File

@ -15,10 +15,28 @@
package caddy
import (
"context"
"encoding/json"
"io"
"testing"
)
func TestContextWithValuePreservesMetricsRegistry(t *testing.T) {
ctx, cancel := NewContext(Context{Context: context.Background()})
t.Cleanup(cancel)
reg := ctx.GetMetricsRegistry()
if reg == nil {
t.Fatal("expected a metrics registry on the base context")
}
type testKey struct{}
derived := ctx.WithValue(testKey{}, "value")
if got := derived.GetMetricsRegistry(); got != reg {
t.Fatalf("WithValue must preserve the metrics registry: got %p, want %p", got, reg)
}
}
func ExampleContext_LoadModule() {
// this whole first part is just setting up for the example;
// note the struct tags - very important; we specify inline_key