You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by vi...@apache.org on 2026/04/29 11:29:27 UTC

(dubbo-go) 01/01: fix(jsonrpc): prevent nil pointer panic on unregistered service path (#3310)

This is an automated email from the ASF dual-hosted git repository.

cvictory pushed a commit to branch task/fix-jsonrpc-nil-panic-3310-20260429-192213
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git

commit 6499e1bb509ed3d02acc912d1da5986cd24dcb47
Author: victory <sh...@alibaba-inc.com>
AuthorDate: Wed Apr 29 19:28:53 2026 +0800

    fix(jsonrpc): prevent nil pointer panic on unregistered service path (#3310)
    
    Check the ok return value from ExporterMap().Load(path) in serveRequest().
    When the path is not registered, return a descriptive "service not found"
    error instead of panicking on a nil type assertion.
    
    Closes #3310
---
 protocol/jsonrpc/server.go      |  5 ++++-
 protocol/jsonrpc/server_test.go | 48 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go
index a863e13b3..5157d523e 100644
--- a/protocol/jsonrpc/server.go
+++ b/protocol/jsonrpc/server.go
@@ -350,7 +350,10 @@ func serveRequest(ctx context.Context, header map[string]string, body []byte, co
 	logger.Debugf("args: %v", args)
 
 	// exporter invoke
-	exporter, _ := jsonrpcProtocol.ExporterMap().Load(path)
+	exporter, ok := jsonrpcProtocol.ExporterMap().Load(path)
+	if !ok {
+		return perrors.Errorf("service not found: %s", path)
+	}
 	invoker := exporter.(*JsonrpcExporter).GetInvoker()
 	if invoker != nil {
 		result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]any{
diff --git a/protocol/jsonrpc/server_test.go b/protocol/jsonrpc/server_test.go
index 14163370f..29ff9bafd 100644
--- a/protocol/jsonrpc/server_test.go
+++ b/protocol/jsonrpc/server_test.go
@@ -22,6 +22,7 @@ import (
 	"io"
 	"net"
 	"net/http"
+	"strconv"
 	"testing"
 	"time"
 )
@@ -51,6 +52,53 @@ func sendHTTPRequest(t *testing.T, conn net.Conn, contentType string) (*http.Res
 	return resp, err
 }
 
+// TestHandlePkg_UnregisteredPath verifies that a valid JSON-RPC request sent
+// to a path with no registered exporter returns an error response instead of
+// panicking with a nil pointer dereference (issue #3310).
+func TestHandlePkg_UnregisteredPath(t *testing.T) {
+	// Ensure the package-level jsonrpcProtocol is initialized, as it would be
+	// in production when the protocol is loaded via extension.
+	GetProtocol()
+
+	serverConn, clientConn := net.Pipe()
+	defer func() {
+		_ = clientConn.Close()
+	}()
+	defer func() {
+		_ = serverConn.Close()
+	}()
+
+	s := NewServer()
+	go s.handlePkg(serverConn)
+
+	jsonBody := `{"jsonrpc":"2.0","method":"SayHello","params":["hello"],"id":1}`
+
+	err := clientConn.SetReadDeadline(time.Now().Add(3 * time.Second))
+	require.NoError(t, err)
+
+	req := "POST /UnregisteredService HTTP/1.1\r\n" +
+		"Host: localhost\r\n" +
+		"Content-Type: application/json\r\n" +
+		"Content-Length: " + strconv.Itoa(len(jsonBody)) + "\r\n" +
+		"\r\n" +
+		jsonBody
+	_, err = clientConn.Write([]byte(req))
+	require.NoError(t, err)
+
+	resp, err := http.ReadResponse(bufio.NewReader(clientConn), nil)
+	require.NoError(t, err, "server should respond without panicking")
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+
+	body, err := io.ReadAll(resp.Body)
+	require.NoError(t, err)
+
+	require.Equal(t, 500, resp.StatusCode)
+	require.Contains(t, string(body), "service not found",
+		"response body should indicate the service was not found")
+}
+
 func TestHandlePkg_ContentType(t *testing.T) {
 	tests := []struct {
 		name            string