This is an automated email from the ASF dual-hosted git repository.
xuetaoli pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu.git
The following commit(s) were added to refs/heads/develop by this push:
new 51050091 fix(filter): use shallow copy of factory config for filters
violating interface to avoid pointer sharing (#815)
51050091 is described below
commit 51050091313ccde9603a39f6dc069b4513bd8bd4
Author: dubbo-go-bot <[email protected]>
AuthorDate: Fri Nov 28 10:28:21 2025 +0800
fix(filter): use shallow copy of factory config for filters violating
interface to avoid pointer sharing (#815)
* fix(filter): deep copy complex filter configs
(https://github.com/dubbo-go-pixiu/dubbo-go-pixiu/pull/38)
Some Pixiu filters have complex configurations (maps, slices, nested
structs). Passing the factory.cfg pointer directly can cause in-flight requests
to read inconsistent or modified configs when FilterManager.ReLoad() updates
factory instances at runtime, violating the HttpFilterFactory interface
contract.
This commit implements DeepCopy() for complex filter configs and updates
PrepareFilterChain to use it. Each filter instance now has its own independent
config, preventing runtime inconsistencies.
Affected files:
- pkg/filter/auth/jwt/jwt.go
- pkg/filter/authority/authority.go
- pkg/filter/authority/config.go
- pkg/filter/cors/cors.go
- pkg/filter/csrf/csrf.go
- pkg/filter/event/event.go
- pkg/filter/failinject/filter.go
- pkg/filter/http/grpcproxy/grpc.go
- pkg/filter/mcp/mcpserver/filter.go
- pkg/filter/sentinel/circuitbreaker/circuit_breaker.go
- pkg/filter/sentinel/config.go
- pkg/filter/sentinel/ratelimit/rate_limit.go
- pkg/model/mcpserver.go
Signed-off-by: Aetherance <[email protected]>
Co-authored-by: Aetherance <[email protected]>
* fix(filter): shallow copy factory config to avoid pointer sharing
(https://github.com/dubbo-go-pixiu/dubbo-go-pixiu/pull/35)
Shallow copy the factory configuration in filters that previously passed
factory pointers directly. This ensures compliance with the
HttpFilterFactory
interface contract and avoids accidental sharing of config between filters
and the factory.
Affected filters (shallow copy sufficient):
- opa/opa.go
- llm/tokenizer/tokenizer.go
- http/loadbalancer/loadbalancer.go
- host/host.go
- header/header.go
- prometheus/metric
- accesslog/access_log.go
Note: Although prometheus/metric.go uses a nested struct
(MetricCollectConfiguration -> MetricCollectRule), all fields are value
types
(string, bool, int). Therefore, shallow copy is sufficient; deep copy is not
required.
Signed-off-by: Aetherance <[email protected]>
Co-authored-by: Aetherance <[email protected]>
---------
Signed-off-by: Aetherance <[email protected]>
Co-authored-by: The_INK <[email protected]>
Co-authored-by: Aetherance <[email protected]>
---
pkg/filter/accesslog/access_log.go | 4 +++-
pkg/filter/header/header.go | 6 ++++--
pkg/filter/host/host.go | 6 ++++--
pkg/filter/http/loadbalancer/loadbalancer.go | 4 +++-
pkg/filter/llm/tokenizer/tokenizer.go | 4 +++-
pkg/filter/opa/opa.go | 4 +++-
pkg/filter/prometheus/metric.go | 4 +++-
7 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/pkg/filter/accesslog/access_log.go
b/pkg/filter/accesslog/access_log.go
index 0b9e1cca..2aa26e0c 100644
--- a/pkg/filter/accesslog/access_log.go
+++ b/pkg/filter/accesslog/access_log.go
@@ -76,8 +76,10 @@ func (p *Plugin) CreateFilterFactory()
(filter.HttpFilterFactory, error) {
// PrepareFilterChain prepare chain when http context init
func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain
filter.FilterChain) error {
+ // Make a shallow copy of the factory config to avoid sharing the
factory's pointer.
+ cpConf := *factory.conf
f := &Filter{
- conf: factory.conf,
+ conf: &cpConf,
alw: factory.alw,
}
chain.AppendDecodeFilters(f)
diff --git a/pkg/filter/header/header.go b/pkg/filter/header/header.go
index 4b078eda..16062a72 100644
--- a/pkg/filter/header/header.go
+++ b/pkg/filter/header/header.go
@@ -56,7 +56,7 @@ func (p *Plugin) Kind() string {
}
func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
- return &FilterFactory{}, nil
+ return &FilterFactory{cfg: &Config{}}, nil
}
func (factory *FilterFactory) Config() any {
@@ -68,7 +68,9 @@ func (factory *FilterFactory) Apply() error {
}
func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain
filter.FilterChain) error {
- f := &Filter{cfg: factory.cfg}
+ // Make a shallow copy of the factory config to avoid sharing the
factory's pointer
+ cfgCopy := *factory.cfg
+ f := &Filter{cfg: &cfgCopy}
chain.AppendDecodeFilters(f)
return nil
}
diff --git a/pkg/filter/host/host.go b/pkg/filter/host/host.go
index 233cf43c..2d7c77b4 100644
--- a/pkg/filter/host/host.go
+++ b/pkg/filter/host/host.go
@@ -55,11 +55,13 @@ func (p *Plugin) Kind() string {
}
func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
- return &FilterFactory{}, nil
+ return &FilterFactory{cfg: &Config{}}, nil
}
func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext,
chain filter.FilterChain) error {
- f := &Filter{cfg: factory.cfg}
+ // Make a shallow copy of the factory config to avoid sharing the
factory's pointer
+ cfgCopy := *factory.cfg
+ f := &Filter{cfg: &cfgCopy}
chain.AppendDecodeFilters(f)
return nil
}
diff --git a/pkg/filter/http/loadbalancer/loadbalancer.go
b/pkg/filter/http/loadbalancer/loadbalancer.go
index ffc6166f..9c4a5933 100644
--- a/pkg/filter/http/loadbalancer/loadbalancer.go
+++ b/pkg/filter/http/loadbalancer/loadbalancer.go
@@ -69,7 +69,9 @@ func (factory *FilterFactory) Apply() error {
}
func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext,
chain filter.FilterChain) error {
- f := &Filter{cfg: factory.cfg}
+ // Make a shallow copy of the factory config to avoid sharing the
factory's pointer
+ cfgCopy := *factory.cfg
+ f := &Filter{cfg: &cfgCopy}
chain.AppendDecodeFilters(f)
return nil
}
diff --git a/pkg/filter/llm/tokenizer/tokenizer.go
b/pkg/filter/llm/tokenizer/tokenizer.go
index bc9d6825..71d0c664 100644
--- a/pkg/filter/llm/tokenizer/tokenizer.go
+++ b/pkg/filter/llm/tokenizer/tokenizer.go
@@ -134,7 +134,9 @@ func (factory *FilterFactory) Config() any { return
factory.cfg }
func (factory *FilterFactory) Apply() error { return registerLLMMetrics() }
func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext,
chain filter.FilterChain) error {
- f := &Filter{cfg: factory.cfg}
+ // Make a shallow copy of the factory config to avoid sharing the
factory's pointer
+ cfgCopy := *factory.cfg
+ f := &Filter{cfg: &cfgCopy}
chain.AppendDecodeFilters(f)
chain.AppendEncodeFilters(f)
return nil
diff --git a/pkg/filter/opa/opa.go b/pkg/filter/opa/opa.go
index 56bae54d..1d6fca3f 100644
--- a/pkg/filter/opa/opa.go
+++ b/pkg/filter/opa/opa.go
@@ -99,7 +99,9 @@ func (factory *FilterFactory) PrepareFilterChain(ctx
*http.HttpContext, chain fi
return fmt.Errorf("failed to prepare OPA query: %w", err)
}
- f := &Filter{cfg: factory.cfg, preparedQuery: &preparedQuery}
+ // Make a shallow copy of the factory config to avoid sharing the
factory's pointer.
+ cfgCopy := *factory.cfg
+ f := &Filter{cfg: &cfgCopy, preparedQuery: &preparedQuery}
chain.AppendDecodeFilters(f)
return nil
}
diff --git a/pkg/filter/prometheus/metric.go b/pkg/filter/prometheus/metric.go
index 08e9e081..ac830530 100644
--- a/pkg/filter/prometheus/metric.go
+++ b/pkg/filter/prometheus/metric.go
@@ -76,8 +76,10 @@ func (factory *FilterFactory) Apply() error {
func (factory *FilterFactory) PrepareFilterChain(ctx *contextHttp.HttpContext,
chain filter.FilterChain) error {
+ // Shallow copy config to avoid pointer sharing (factory.cfg may change
at runtime)
+ cpConfig := *factory.Cfg
f := &Filter{
- Cfg: factory.Cfg,
+ Cfg: &cpConfig,
Prom: factory.Prom,
}
f.Prom.SetPushGatewayUrl(f.Cfg.Rules.PushGatewayURL,
f.Cfg.Rules.MetricPath)