gRPC client: Strip "grpc-go/version" suffix from User-Agent header (#5689)

Fixes https://github.com/XTLS/Xray-core/pull/5658#issuecomment-3894269376

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
This commit is contained in:
Copilot
2026-02-13 19:49:47 +00:00
committed by GitHub
parent 6a909b2507
commit b43276c6d3
2 changed files with 31 additions and 8 deletions

View File

@@ -1,9 +1,12 @@
package grpc
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func TestConfig_GetServiceName(t *testing.T) {
@@ -115,3 +118,12 @@ func TestConfig_GetTunMultiStreamName(t *testing.T) {
})
}
}
func TestSetUserAgent(t *testing.T) {
ua := "Test/1.0"
conn, err := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUserAgent(ua))
assert.NoError(t, err)
defer conn.Close()
setUserAgent(conn, ua)
assert.Equal(t, ua, reflect.ValueOf(conn).Elem().FieldByName("dopts").FieldByName("copts").FieldByName("UserAgent").String())
}

View File

@@ -2,6 +2,7 @@ package grpc
import (
"context"
"reflect"
"sync"
"time"
@@ -168,12 +169,6 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
dialOptions = append(dialOptions, grpc.WithInitialWindowSize(grpcSettings.InitialWindowsSize))
}
userAgent := grpcSettings.UserAgent
if userAgent == "" {
userAgent = utils.ChromeUA
}
dialOptions = append(dialOptions, grpc.WithUserAgent(userAgent))
var grpcDestHost string
if dest.Address.Family().IsDomain() {
grpcDestHost = dest.Address.Domain()
@@ -181,10 +176,26 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
grpcDestHost = dest.Address.IP().String()
}
conn, err := grpc.Dial(
net.JoinHostPort(grpcDestHost, dest.Port.String()),
conn, err := grpc.NewClient(
"passthrough:///"+net.JoinHostPort(grpcDestHost, dest.Port.String()),
dialOptions...,
)
if err == nil {
userAgent := grpcSettings.UserAgent
if userAgent == "" {
userAgent = utils.ChromeUA
}
setUserAgent(conn, userAgent)
conn.Connect()
}
globalDialerMap[dialerConf{dest, streamSettings}] = conn
return conn, err
}
// setUserAgent overrides the user-agent on a ClientConn to remove the
// "grpc-go/version" suffix that grpc.WithUserAgent unconditionally appends.
func setUserAgent(conn *grpc.ClientConn, ua string) {
if f := reflect.ValueOf(conn).Elem().FieldByName("dopts").FieldByName("copts").FieldByName("UserAgent"); f.IsValid() {
*(*string)(f.Addr().UnsafePointer()) = ua
}
}