@@ -31,7 +31,10 @@ import (
3131 "github.com/golang/protobuf/proto"
3232 "google.golang.org/grpc"
3333 "google.golang.org/grpc/credentials/insecure"
34+ "google.golang.org/grpc/internal"
3435 "google.golang.org/grpc/internal/grpctest"
36+ "google.golang.org/grpc/internal/stubserver"
37+ "google.golang.org/grpc/internal/testutils"
3538 "google.golang.org/grpc/metadata"
3639 "google.golang.org/grpc/stats"
3740 "google.golang.org/grpc/status"
@@ -1457,3 +1460,61 @@ func (s) TestMultipleServerStatsHandler(t *testing.T) {
14571460 t .Fatalf ("h.gotConn: unexpected amount of ConnStats: %v != %v" , len (h .gotConn ), 4 )
14581461 }
14591462}
1463+
1464+ // TestStatsHandlerCallsServerIsRegisteredMethod tests whether a stats handler
1465+ // gets access to a Server on the server side, and thus the method that the
1466+ // server owns which specifies whether a method is made or not. The test sets up
1467+ // a server with a unary call and full duplex call configured, and makes an RPC.
1468+ // Within the stats handler, asking the server whether unary or duplex method
1469+ // names are registered should return true, and any other query should return
1470+ // false.
1471+ func (s ) TestStatsHandlerCallsServerIsRegisteredMethod (t * testing.T ) {
1472+ wg := sync.WaitGroup {}
1473+ wg .Add (1 )
1474+ stubStatsHandler := & testutils.StubStatsHandler {
1475+ TagRPCF : func (ctx context.Context , _ * stats.RPCTagInfo ) context.Context {
1476+ // OpenTelemetry instrumentation needs the passed in Server to determine if
1477+ // methods are registered in different handle calls in to record metrics.
1478+ // This tag RPC call context gets passed into every handle call, so can
1479+ // assert once here, since it maps to all the handle RPC calls that come
1480+ // after. These internal calls will be how the OpenTelemetry instrumentation
1481+ // component accesses this server and the subsequent helper on the server.
1482+ server := internal .ServerFromContext .(func (context.Context ) * grpc.Server )(ctx )
1483+ if server == nil {
1484+ t .Errorf ("stats handler received ctx has no server present" )
1485+ }
1486+ isRegisteredMethod := internal .IsRegisteredMethod .(func (* grpc.Server , string ) bool )
1487+ // /s/m and s/m are valid.
1488+ if ! isRegisteredMethod (server , "/grpc.testing.TestService/UnaryCall" ) {
1489+ t .Errorf ("UnaryCall should be a registered method according to server" )
1490+ }
1491+ if ! isRegisteredMethod (server , "grpc.testing.TestService/FullDuplexCall" ) {
1492+ t .Errorf ("FullDuplexCall should be a registered method according to server" )
1493+ }
1494+ if isRegisteredMethod (server , "/grpc.testing.TestService/DoesNotExistCall" ) {
1495+ t .Errorf ("DoesNotExistCall should not be a registered method according to server" )
1496+ }
1497+ if isRegisteredMethod (server , "/unknownService/UnaryCall" ) {
1498+ t .Errorf ("/unknownService/UnaryCall should not be a registered method according to server" )
1499+ }
1500+ wg .Done ()
1501+ return ctx
1502+ },
1503+ }
1504+ ss := & stubserver.StubServer {
1505+ UnaryCallF : func (ctx context.Context , in * testpb.SimpleRequest ) (* testpb.SimpleResponse , error ) {
1506+ return & testpb.SimpleResponse {}, nil
1507+ },
1508+ }
1509+ if err := ss .Start ([]grpc.ServerOption {grpc .StatsHandler (stubStatsHandler )}); err != nil {
1510+ t .Fatalf ("Error starting endpoint server: %v" , err )
1511+ }
1512+ defer ss .Stop ()
1513+
1514+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
1515+ defer cancel ()
1516+ if _ , err := ss .Client .UnaryCall (ctx , & testpb.SimpleRequest {Payload : & testpb.Payload {}}); err != nil {
1517+ t .Fatalf ("Unexpected error from UnaryCall: %v" , err )
1518+ }
1519+ wg .Wait ()
1520+ }
0 commit comments