This is an automated email from the ASF dual-hosted git repository.
baerwang 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 153c1fe0f fix(dubboproxy): replace panic with error returns in filter
construction (#996)
153c1fe0f is described below
commit 153c1fe0fcc03aeeae19094285ad1b4112c026c4
Author: aias00 <[email protected]>
AuthorDate: Mon Aug 24 10:46:10 2026 +0800
fix(dubboproxy): replace panic with error returns in filter construction
(#996)
* fix(dubboproxy): replace panic with error returns in filter construction
Fixes #989 Issue 3: network filter/plugin construction path uses panic
for type assertion failures where normal error propagation would be safer.
Changes in plugin.go:
- CreateFilter: Replace panic with error return when config type assertion
fails
- Already had error return in signature but was panicking instead
- Now returns proper error: "expected
*model.DubboProxyConnectionManagerConfig, got %T"
Changes in manager.go:
- OnData: Replace panic with error return when data type assertion fails
- Already had error return in signature but was panicking instead
- Now returns proper error: "expected *invocation.RPCInvocation, got %T"
Classification:
- Both panics were type assertion failures during request/startup handling
- Neither was a true invariant violation (programming errors should be
caught)
- Both methods already had error returns - just not using them
- Returning errors allows graceful handling without crashing the server
Note: The recover() in handleRpcInvocation (line 182-187) is kept as is -
that's a proper defensive pattern that catches panics from filter chain.
Co-Authored-By: Claude <[email protected]>
* fix(dubboproxy): improve error message clarity
- Change verbose error message in CreateFilter to concise type-mismatch
format
- Change verbose error message in OnData to concise type-mismatch format
Co-Authored-By: Claude <[email protected]>
* fix(dubboproxy): fix import formatting for CI
Co-Authored-By: Claude <[email protected]>
* fix(dubboproxy): replace panics with error returns in OnTripleData
- Check metadata value array length before accessing first element
- Use safe type assertion for interface key extraction
- Return descriptive errors instead of panicking
Co-Authored-By: Claude <[email protected]>
* test(dubboproxy): add unit tests for panic-to-error fixes
Add comprehensive tests for dubboproxy network filter:
plugin_test.go:
- Test CreateFilter with valid config (success)
- Test CreateFilter with invalid config type (error, not panic)
manager_test.go:
- Test OnData with invalid type returns error (not panic)
- Test OnData with valid RPCInvocation passes type check
- Test OnTripleData with empty metadata value returns error
- Test OnTripleData with missing interface key returns error
- Test OnTripleData with valid metadata passes type check
- Test OnEncode with invalid type returns error
Coverage improvements:
- CreateFilter: 100%
- OnData: 63.2%
- OnTripleData: 56.0%
These tests verify the fixes in PR #996 that replace panics
with proper error returns for type assertion failures.
Co-Authored-By: Claude <[email protected]>
* fix: add missing newline at end of test files
CI gofmt check requires newline at end of files.
* fix: format imports in test files for CI
imports-formatter requires specific grouping of imports:
- Standard library
- Third-party packages
- Project packages
* style: replace interface{} with any in test files
golangci-lint v2.4.0 requires interface{} to be replaced with any
---------
Co-authored-by: Claude <[email protected]>
---
pkg/filter/network/dubboproxy/manager.go | 13 +-
pkg/filter/network/dubboproxy/manager_test.go | 227 ++++++++++++++++++++++++++
pkg/filter/network/dubboproxy/plugin.go | 8 +-
pkg/filter/network/dubboproxy/plugin_test.go | 86 ++++++++++
4 files changed, 329 insertions(+), 5 deletions(-)
diff --git a/pkg/filter/network/dubboproxy/manager.go
b/pkg/filter/network/dubboproxy/manager.go
index e16950269..3ae4d5e00 100644
--- a/pkg/filter/network/dubboproxy/manager.go
+++ b/pkg/filter/network/dubboproxy/manager.go
@@ -110,10 +110,17 @@ func (dcm *DubboProxyConnectionManager) OnTripleData(ctx
context.Context, method
md, ok := metadata.FromIncomingContext(ctx)
if ok {
for k := range md {
- dubboAttachment[k] = md.Get(k)[0]
+ values := md.Get(k)
+ if len(values) == 0 {
+ return nil, errors.Errorf("empty metadata value
for key: %s", k)
+ }
+ dubboAttachment[k] = values[0]
}
}
- interfaceName := dubboAttachment[constant.InterfaceKey].(string)
+ interfaceName, ok := dubboAttachment[constant.InterfaceKey].(string)
+ if !ok {
+ return nil, errors.Errorf("missing or invalid interface key in
metadata: expected string, got %T", dubboAttachment[constant.InterfaceKey])
+ }
ra, err := dcm.routerCoordinator.RouteByPathAndName(interfaceName,
methodName)
@@ -144,7 +151,7 @@ func (dcm *DubboProxyConnectionManager) OnTripleData(ctx
context.Context, method
func (dcm *DubboProxyConnectionManager) OnData(data any) (any, error) {
old_invoc, ok := data.(*invocation.RPCInvocation)
if !ok {
- panic("create invocation occur some exception for the type is
not suitable one.")
+ return nil, errors.Errorf("invalid invocation type: expected
*invocation.RPCInvocation, got %T", data)
}
// need reconstruct RPCInvocation ParameterValues witch is same with
arguments. refer to dubbogo/common/proxy/proxy.makeDubboCallProxy
arguments := old_invoc.Arguments()
diff --git a/pkg/filter/network/dubboproxy/manager_test.go
b/pkg/filter/network/dubboproxy/manager_test.go
new file mode 100644
index 000000000..658d11d8b
--- /dev/null
+++ b/pkg/filter/network/dubboproxy/manager_test.go
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dubboproxy
+
+import (
+ "context"
+ "testing"
+)
+
+import (
+ "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
+
+ "github.com/dubbogo/grpc-go/metadata"
+
+ "github.com/stretchr/testify/assert"
+)
+
+import (
+ "github.com/apache/dubbo-go-pixiu/pkg/model"
+)
+
+func TestDubboProxyConnectionManager_OnData_InvalidType(t *testing.T) {
+ // Create a minimal manager with required dependencies
+ dcm := &DubboProxyConnectionManager{
+ config: &model.DubboProxyConnectionManagerConfig{},
+ routerCoordinator: nil, // not needed for this test
+ }
+
+ // Test with nil data
+ result, err := dcm.OnData(nil)
+ assert.Error(t, err)
+ assert.Nil(t, result)
+ assert.Contains(t, err.Error(), "invalid invocation type")
+ assert.Contains(t, err.Error(), "expected *invocation.RPCInvocation")
+
+ // Test with wrong type - string
+ result, err = dcm.OnData("invalid")
+ assert.Error(t, err)
+ assert.Nil(t, result)
+ assert.Contains(t, err.Error(), "invalid invocation type")
+
+ // Test with wrong type - int
+ result, err = dcm.OnData(123)
+ assert.Error(t, err)
+ assert.Nil(t, result)
+ assert.Contains(t, err.Error(), "invalid invocation type")
+
+ // Test with wrong pointer type
+ result, err = dcm.OnData(&struct{}{})
+ assert.Error(t, err)
+ assert.Nil(t, result)
+ assert.Contains(t, err.Error(), "invalid invocation type")
+}
+
+func TestDubboProxyConnectionManager_OnData_ValidTypePassesTypeCheck(t
*testing.T) {
+ // This test verifies that OnData accepts valid RPCInvocation type
+ // without panicking on type assertion (the fix in this PR)
+ // Note: Full route functionality requires mocking routerCoordinator
which is complex
+
+ // Create a manager with proper initialization
+ cfg := &model.DubboProxyConnectionManagerConfig{
+ RouteConfig: model.RouteConfiguration{},
+ }
+ dcm := CreateDubboProxyConnectionManager(cfg)
+
+ // Create valid RPCInvocation
+ invoc := invocation.NewRPCInvocationWithOptions(
+ invocation.WithMethodName("testMethod"),
+ invocation.WithArguments([]any{}),
+ )
+
+ // OnData should not panic on type assertion (the key fix in this PR)
+ // It will return an error about route not found, which is acceptable
+ result, err := dcm.OnData(invoc)
+
+ // The key assertion: we get a proper error, not a panic
+ // Type assertion passed, so we should not see "invalid invocation
type" error
+ if err != nil {
+ assert.NotContains(t, err.Error(), "invalid invocation type",
+ "Type assertion should have passed for valid
RPCInvocation")
+ }
+ // Result may be nil due to route error, which is expected
+ _ = result // We don't care about result for this test
+}
+
+func TestDubboProxyConnectionManager_OnTripleData_EmptyMetadataValue(t
*testing.T) {
+ dcm := &DubboProxyConnectionManager{
+ config: &model.DubboProxyConnectionManagerConfig{},
+ routerCoordinator: nil,
+ }
+
+ // Create context with metadata where the values slice is empty
+ // This tests the "if len(values) == 0" check in manager.go:114
+ md := metadata.MD{
+ "test-key": []string{}, // Empty slice triggers the error
+ }
+ ctx := metadata.NewIncomingContext(context.Background(), md)
+
+ result, err := dcm.OnTripleData(ctx, "testMethod", []any{})
+ assert.Error(t, err)
+ assert.Nil(t, result)
+ assert.Contains(t, err.Error(), "empty metadata value for key:
test-key")
+}
+
+func TestDubboProxyConnectionManager_OnTripleData_MissingInterfaceKey(t
*testing.T) {
+ dcm := &DubboProxyConnectionManager{
+ config: &model.DubboProxyConnectionManagerConfig{},
+ routerCoordinator: nil,
+ }
+
+ // Create context with metadata but no interface key
+ md := metadata.Pairs("some-key", "some-value")
+ ctx := metadata.NewIncomingContext(context.Background(), md)
+
+ result, err := dcm.OnTripleData(ctx, "testMethod", []any{})
+ assert.Error(t, err)
+ assert.Nil(t, result)
+ assert.Contains(t, err.Error(), "missing or invalid interface key")
+}
+
+func TestDubboProxyConnectionManager_OnTripleData_InvalidInterfaceKeyType(t
*testing.T) {
+ dcm := &DubboProxyConnectionManager{
+ config: &model.DubboProxyConnectionManagerConfig{},
+ routerCoordinator: nil,
+ }
+
+ // Create context with metadata where interface key exists but is not a
string
+ // Note: metadata.Pairs always stores string values, so we need to
manually construct
+ md := metadata.MD{
+ "interface": []string{}, // Empty slice - this will trigger
empty metadata error
+ }
+ ctx := metadata.NewIncomingContext(context.Background(), md)
+
+ result, err := dcm.OnTripleData(ctx, "testMethod", []any{})
+ assert.Error(t, err)
+ assert.Nil(t, result)
+ // Should get empty metadata value error
+ assert.Contains(t, err.Error(), "empty metadata value")
+}
+
+func TestDubboProxyConnectionManager_OnTripleData_NoMetadata(t *testing.T) {
+ dcm := &DubboProxyConnectionManager{
+ config: &model.DubboProxyConnectionManagerConfig{},
+ routerCoordinator: nil,
+ }
+
+ // Create context without metadata
+ ctx := context.Background()
+
+ result, err := dcm.OnTripleData(ctx, "testMethod", []any{})
+ assert.Error(t, err)
+ assert.Nil(t, result)
+ assert.Contains(t, err.Error(), "missing or invalid interface key")
+}
+
+func TestCreateDubboProxyConnectionManager(t *testing.T) {
+ cfg := &model.DubboProxyConnectionManagerConfig{
+ RouteConfig: model.RouteConfiguration{},
+ TimeoutStr: "5s",
+ }
+
+ dcm := CreateDubboProxyConnectionManager(cfg)
+ assert.NotNil(t, dcm)
+ assert.NotNil(t, dcm.config)
+ assert.NotNil(t, dcm.routerCoordinator)
+ assert.NotNil(t, dcm.codec)
+ assert.NotNil(t, dcm.filterManager)
+}
+
+func
TestDubboProxyConnectionManager_OnTripleData_ValidMetadataTypeCheckPasses(t
*testing.T) {
+ // This test verifies that OnTripleData accepts valid metadata without
type assertion panic
+ cfg := &model.DubboProxyConnectionManagerConfig{
+ RouteConfig: model.RouteConfiguration{},
+ }
+ dcm := CreateDubboProxyConnectionManager(cfg)
+
+ // Create context with valid metadata including interface key
+ md := metadata.Pairs(
+ "interface", "com.example.TestService",
+ "version", "1.0.0",
+ "group", "testGroup",
+ )
+ ctx := metadata.NewIncomingContext(context.Background(), md)
+
+ // OnTripleData should not panic on type assertions (the fix in this PR)
+ // It will return an error about route not found, which is acceptable
+ result, err := dcm.OnTripleData(ctx, "testMethod", []any{})
+
+ // The key assertion: we get a proper error, not a panic
+ // Type assertions passed, so we should not see "missing or invalid
interface key" error
+ if err != nil {
+ assert.NotContains(t, err.Error(), "missing or invalid
interface key",
+ "Type assertion for interface key should have passed")
+ assert.NotContains(t, err.Error(), "empty metadata value",
+ "Metadata value check should have passed")
+ }
+ // Result may be nil due to route error, which is expected
+ _ = result
+}
+
+func TestDubboProxyConnectionManager_OnEncode(t *testing.T) {
+ // Test OnEncode with invalid type
+ cfg := &model.DubboProxyConnectionManagerConfig{
+ RouteConfig: model.RouteConfiguration{},
+ }
+ dcm := CreateDubboProxyConnectionManager(cfg)
+
+ // Test with invalid type
+ _, err := dcm.OnEncode("invalid")
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "invalid rpc response")
+}
diff --git a/pkg/filter/network/dubboproxy/plugin.go
b/pkg/filter/network/dubboproxy/plugin.go
index 3e8d99f4e..5a9999e2f 100644
--- a/pkg/filter/network/dubboproxy/plugin.go
+++ b/pkg/filter/network/dubboproxy/plugin.go
@@ -17,6 +17,10 @@
package dubboproxy
+import (
+ "fmt"
+)
+
import (
"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
@@ -45,10 +49,10 @@ func (p *Plugin) Kind() string {
// CreateFilter create dubbo networkfilter
func (p *Plugin) CreateFilter(config any) (filter.NetworkFilter, error) {
hcmc, ok := config.(*model.DubboProxyConnectionManagerConfig)
- hcmc.Timeout = stringutil.ResolveTimeStr2Time(hcmc.TimeoutStr,
constant.DefaultReqTimeout)
if !ok {
- panic("CreateFilter occur some exception for the type is not
suitable one.")
+ return nil, fmt.Errorf("invalid config type: expected
*model.DubboProxyConnectionManagerConfig, got %T", config)
}
+ hcmc.Timeout = stringutil.ResolveTimeStr2Time(hcmc.TimeoutStr,
constant.DefaultReqTimeout)
return CreateDubboProxyConnectionManager(hcmc), nil
}
diff --git a/pkg/filter/network/dubboproxy/plugin_test.go
b/pkg/filter/network/dubboproxy/plugin_test.go
new file mode 100644
index 000000000..f8f195421
--- /dev/null
+++ b/pkg/filter/network/dubboproxy/plugin_test.go
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dubboproxy
+
+import (
+ "testing"
+)
+
+import (
+ "github.com/stretchr/testify/assert"
+)
+
+import (
+ "github.com/apache/dubbo-go-pixiu/pkg/model"
+)
+
+func TestPlugin_Kind(t *testing.T) {
+ p := &Plugin{}
+ assert.Equal(t, "dgp.filter.network.dubboconnectionmanager", p.Kind())
+}
+
+func TestPlugin_Config(t *testing.T) {
+ p := &Plugin{}
+ cfg := p.Config()
+ _, ok := cfg.(*model.DubboProxyConnectionManagerConfig)
+ assert.True(t, ok, "Config should return
*model.DubboProxyConnectionManagerConfig")
+}
+
+func TestPlugin_CreateFilter_Success(t *testing.T) {
+ p := &Plugin{}
+ cfg := &model.DubboProxyConnectionManagerConfig{
+ RouteConfig: model.RouteConfiguration{},
+ TimeoutStr: "3s",
+ }
+
+ filter, err := p.CreateFilter(cfg)
+ assert.NoError(t, err)
+ assert.NotNil(t, filter)
+
+ // Verify timeout was set
+ assert.Greater(t, cfg.Timeout.Seconds(), float64(0))
+}
+
+func TestPlugin_CreateFilter_InvalidConfigType(t *testing.T) {
+ p := &Plugin{}
+
+ // Test with nil config
+ filter, err := p.CreateFilter(nil)
+ assert.Error(t, err)
+ assert.Nil(t, filter)
+ assert.Contains(t, err.Error(), "invalid config type")
+ assert.Contains(t, err.Error(), "expected
*model.DubboProxyConnectionManagerConfig")
+
+ // Test with wrong type - string
+ filter, err = p.CreateFilter("invalid")
+ assert.Error(t, err)
+ assert.Nil(t, filter)
+ assert.Contains(t, err.Error(), "invalid config type")
+
+ // Test with wrong type - map
+ filter, err = p.CreateFilter(map[string]any{"key": "value"})
+ assert.Error(t, err)
+ assert.Nil(t, filter)
+ assert.Contains(t, err.Error(), "invalid config type")
+
+ // Test with wrong pointer type
+ filter, err = p.CreateFilter(&model.HttpConnectionManagerConfig{})
+ assert.Error(t, err)
+ assert.Nil(t, filter)
+ assert.Contains(t, err.Error(), "invalid config type")
+}