google.golang.org/grpc@v1.62.1/reflection/serverreflection.go (about)

     1  /*
     2   *
     3   * Copyright 2016 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  /*
    20  Package reflection implements server reflection service.
    21  
    22  The service implemented is defined in:
    23  https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1alpha/reflection.proto.
    24  
    25  To register server reflection on a gRPC server:
    26  
    27  	import "google.golang.org/grpc/reflection"
    28  
    29  	s := grpc.NewServer()
    30  	pb.RegisterYourOwnServer(s, &server{})
    31  
    32  	// Register reflection service on gRPC server.
    33  	reflection.Register(s)
    34  
    35  	s.Serve(lis)
    36  */
    37  package reflection // import "google.golang.org/grpc/reflection"
    38  
    39  import (
    40  	"io"
    41  	"sort"
    42  
    43  	"google.golang.org/grpc"
    44  	"google.golang.org/grpc/codes"
    45  	"google.golang.org/grpc/status"
    46  	"google.golang.org/protobuf/proto"
    47  	"google.golang.org/protobuf/reflect/protodesc"
    48  	"google.golang.org/protobuf/reflect/protoreflect"
    49  	"google.golang.org/protobuf/reflect/protoregistry"
    50  
    51  	v1reflectiongrpc "google.golang.org/grpc/reflection/grpc_reflection_v1"
    52  	v1reflectionpb "google.golang.org/grpc/reflection/grpc_reflection_v1"
    53  	v1alphareflectiongrpc "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
    54  )
    55  
    56  // GRPCServer is the interface provided by a gRPC server. It is implemented by
    57  // *grpc.Server, but could also be implemented by other concrete types. It acts
    58  // as a registry, for accumulating the services exposed by the server.
    59  type GRPCServer interface {
    60  	grpc.ServiceRegistrar
    61  	ServiceInfoProvider
    62  }
    63  
    64  var _ GRPCServer = (*grpc.Server)(nil)
    65  
    66  // Register registers the server reflection service on the given gRPC server.
    67  // Both the v1 and v1alpha versions are registered.
    68  func Register(s GRPCServer) {
    69  	svr := NewServerV1(ServerOptions{Services: s})
    70  	v1alphareflectiongrpc.RegisterServerReflectionServer(s, asV1Alpha(svr))
    71  	v1reflectiongrpc.RegisterServerReflectionServer(s, svr)
    72  }
    73  
    74  // RegisterV1 registers only the v1 version of the server reflection service
    75  // on the given gRPC server. Many clients may only support v1alpha so most
    76  // users should use Register instead, at least until clients have upgraded.
    77  func RegisterV1(s GRPCServer) {
    78  	svr := NewServerV1(ServerOptions{Services: s})
    79  	v1reflectiongrpc.RegisterServerReflectionServer(s, svr)
    80  }
    81  
    82  // ServiceInfoProvider is an interface used to retrieve metadata about the
    83  // services to expose.
    84  //
    85  // The reflection service is only interested in the service names, but the
    86  // signature is this way so that *grpc.Server implements it. So it is okay
    87  // for a custom implementation to return zero values for the
    88  // grpc.ServiceInfo values in the map.
    89  //
    90  // # Experimental
    91  //
    92  // Notice: This type is EXPERIMENTAL and may be changed or removed in a
    93  // later release.
    94  type ServiceInfoProvider interface {
    95  	GetServiceInfo() map[string]grpc.ServiceInfo
    96  }
    97  
    98  // ExtensionResolver is the interface used to query details about extensions.
    99  // This interface is satisfied by protoregistry.GlobalTypes.
   100  //
   101  // # Experimental
   102  //
   103  // Notice: This type is EXPERIMENTAL and may be changed or removed in a
   104  // later release.
   105  type ExtensionResolver interface {
   106  	protoregistry.ExtensionTypeResolver
   107  	RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool)
   108  }
   109  
   110  // ServerOptions represents the options used to construct a reflection server.
   111  //
   112  // # Experimental
   113  //
   114  // Notice: This type is EXPERIMENTAL and may be changed or removed in a
   115  // later release.
   116  type ServerOptions struct {
   117  	// The source of advertised RPC services. If not specified, the reflection
   118  	// server will report an empty list when asked to list services.
   119  	//
   120  	// This value will typically be a *grpc.Server. But the set of advertised
   121  	// services can be customized by wrapping a *grpc.Server or using an
   122  	// alternate implementation that returns a custom set of service names.
   123  	Services ServiceInfoProvider
   124  	// Optional resolver used to load descriptors. If not specified,
   125  	// protoregistry.GlobalFiles will be used.
   126  	DescriptorResolver protodesc.Resolver
   127  	// Optional resolver used to query for known extensions. If not specified,
   128  	// protoregistry.GlobalTypes will be used.
   129  	ExtensionResolver ExtensionResolver
   130  }
   131  
   132  // NewServer returns a reflection server implementation using the given options.
   133  // This can be used to customize behavior of the reflection service. Most usages
   134  // should prefer to use Register instead. For backwards compatibility reasons,
   135  // this returns the v1alpha version of the reflection server. For a v1 version
   136  // of the reflection server, see NewServerV1.
   137  //
   138  // # Experimental
   139  //
   140  // Notice: This function is EXPERIMENTAL and may be changed or removed in a
   141  // later release.
   142  func NewServer(opts ServerOptions) v1alphareflectiongrpc.ServerReflectionServer {
   143  	return asV1Alpha(NewServerV1(opts))
   144  }
   145  
   146  // NewServerV1 returns a reflection server implementation using the given options.
   147  // This can be used to customize behavior of the reflection service. Most usages
   148  // should prefer to use Register instead.
   149  //
   150  // # Experimental
   151  //
   152  // Notice: This function is EXPERIMENTAL and may be changed or removed in a
   153  // later release.
   154  func NewServerV1(opts ServerOptions) v1reflectiongrpc.ServerReflectionServer {
   155  	if opts.DescriptorResolver == nil {
   156  		opts.DescriptorResolver = protoregistry.GlobalFiles
   157  	}
   158  	if opts.ExtensionResolver == nil {
   159  		opts.ExtensionResolver = protoregistry.GlobalTypes
   160  	}
   161  	return &serverReflectionServer{
   162  		s:            opts.Services,
   163  		descResolver: opts.DescriptorResolver,
   164  		extResolver:  opts.ExtensionResolver,
   165  	}
   166  }
   167  
   168  type serverReflectionServer struct {
   169  	v1alphareflectiongrpc.UnimplementedServerReflectionServer
   170  	s            ServiceInfoProvider
   171  	descResolver protodesc.Resolver
   172  	extResolver  ExtensionResolver
   173  }
   174  
   175  // fileDescWithDependencies returns a slice of serialized fileDescriptors in
   176  // wire format ([]byte). The fileDescriptors will include fd and all the
   177  // transitive dependencies of fd with names not in sentFileDescriptors.
   178  func (s *serverReflectionServer) fileDescWithDependencies(fd protoreflect.FileDescriptor, sentFileDescriptors map[string]bool) ([][]byte, error) {
   179  	if fd.IsPlaceholder() {
   180  		// If the given root file is a placeholder, treat it
   181  		// as missing instead of serializing it.
   182  		return nil, protoregistry.NotFound
   183  	}
   184  	var r [][]byte
   185  	queue := []protoreflect.FileDescriptor{fd}
   186  	for len(queue) > 0 {
   187  		currentfd := queue[0]
   188  		queue = queue[1:]
   189  		if currentfd.IsPlaceholder() {
   190  			// Skip any missing files in the dependency graph.
   191  			continue
   192  		}
   193  		if sent := sentFileDescriptors[currentfd.Path()]; len(r) == 0 || !sent {
   194  			sentFileDescriptors[currentfd.Path()] = true
   195  			fdProto := protodesc.ToFileDescriptorProto(currentfd)
   196  			currentfdEncoded, err := proto.Marshal(fdProto)
   197  			if err != nil {
   198  				return nil, err
   199  			}
   200  			r = append(r, currentfdEncoded)
   201  		}
   202  		for i := 0; i < currentfd.Imports().Len(); i++ {
   203  			queue = append(queue, currentfd.Imports().Get(i))
   204  		}
   205  	}
   206  	return r, nil
   207  }
   208  
   209  // fileDescEncodingContainingSymbol finds the file descriptor containing the
   210  // given symbol, finds all of its previously unsent transitive dependencies,
   211  // does marshalling on them, and returns the marshalled result. The given symbol
   212  // can be a type, a service or a method.
   213  func (s *serverReflectionServer) fileDescEncodingContainingSymbol(name string, sentFileDescriptors map[string]bool) ([][]byte, error) {
   214  	d, err := s.descResolver.FindDescriptorByName(protoreflect.FullName(name))
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	return s.fileDescWithDependencies(d.ParentFile(), sentFileDescriptors)
   219  }
   220  
   221  // fileDescEncodingContainingExtension finds the file descriptor containing
   222  // given extension, finds all of its previously unsent transitive dependencies,
   223  // does marshalling on them, and returns the marshalled result.
   224  func (s *serverReflectionServer) fileDescEncodingContainingExtension(typeName string, extNum int32, sentFileDescriptors map[string]bool) ([][]byte, error) {
   225  	xt, err := s.extResolver.FindExtensionByNumber(protoreflect.FullName(typeName), protoreflect.FieldNumber(extNum))
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	return s.fileDescWithDependencies(xt.TypeDescriptor().ParentFile(), sentFileDescriptors)
   230  }
   231  
   232  // allExtensionNumbersForTypeName returns all extension numbers for the given type.
   233  func (s *serverReflectionServer) allExtensionNumbersForTypeName(name string) ([]int32, error) {
   234  	var numbers []int32
   235  	s.extResolver.RangeExtensionsByMessage(protoreflect.FullName(name), func(xt protoreflect.ExtensionType) bool {
   236  		numbers = append(numbers, int32(xt.TypeDescriptor().Number()))
   237  		return true
   238  	})
   239  	sort.Slice(numbers, func(i, j int) bool {
   240  		return numbers[i] < numbers[j]
   241  	})
   242  	if len(numbers) == 0 {
   243  		// maybe return an error if given type name is not known
   244  		if _, err := s.descResolver.FindDescriptorByName(protoreflect.FullName(name)); err != nil {
   245  			return nil, err
   246  		}
   247  	}
   248  	return numbers, nil
   249  }
   250  
   251  // listServices returns the names of services this server exposes.
   252  func (s *serverReflectionServer) listServices() []*v1reflectionpb.ServiceResponse {
   253  	serviceInfo := s.s.GetServiceInfo()
   254  	resp := make([]*v1reflectionpb.ServiceResponse, 0, len(serviceInfo))
   255  	for svc := range serviceInfo {
   256  		resp = append(resp, &v1reflectionpb.ServiceResponse{Name: svc})
   257  	}
   258  	sort.Slice(resp, func(i, j int) bool {
   259  		return resp[i].Name < resp[j].Name
   260  	})
   261  	return resp
   262  }
   263  
   264  // ServerReflectionInfo is the reflection service handler.
   265  func (s *serverReflectionServer) ServerReflectionInfo(stream v1reflectiongrpc.ServerReflection_ServerReflectionInfoServer) error {
   266  	sentFileDescriptors := make(map[string]bool)
   267  	for {
   268  		in, err := stream.Recv()
   269  		if err == io.EOF {
   270  			return nil
   271  		}
   272  		if err != nil {
   273  			return err
   274  		}
   275  
   276  		out := &v1reflectionpb.ServerReflectionResponse{
   277  			ValidHost:       in.Host,
   278  			OriginalRequest: in,
   279  		}
   280  		switch req := in.MessageRequest.(type) {
   281  		case *v1reflectionpb.ServerReflectionRequest_FileByFilename:
   282  			var b [][]byte
   283  			fd, err := s.descResolver.FindFileByPath(req.FileByFilename)
   284  			if err == nil {
   285  				b, err = s.fileDescWithDependencies(fd, sentFileDescriptors)
   286  			}
   287  			if err != nil {
   288  				out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{
   289  					ErrorResponse: &v1reflectionpb.ErrorResponse{
   290  						ErrorCode:    int32(codes.NotFound),
   291  						ErrorMessage: err.Error(),
   292  					},
   293  				}
   294  			} else {
   295  				out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_FileDescriptorResponse{
   296  					FileDescriptorResponse: &v1reflectionpb.FileDescriptorResponse{FileDescriptorProto: b},
   297  				}
   298  			}
   299  		case *v1reflectionpb.ServerReflectionRequest_FileContainingSymbol:
   300  			b, err := s.fileDescEncodingContainingSymbol(req.FileContainingSymbol, sentFileDescriptors)
   301  			if err != nil {
   302  				out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{
   303  					ErrorResponse: &v1reflectionpb.ErrorResponse{
   304  						ErrorCode:    int32(codes.NotFound),
   305  						ErrorMessage: err.Error(),
   306  					},
   307  				}
   308  			} else {
   309  				out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_FileDescriptorResponse{
   310  					FileDescriptorResponse: &v1reflectionpb.FileDescriptorResponse{FileDescriptorProto: b},
   311  				}
   312  			}
   313  		case *v1reflectionpb.ServerReflectionRequest_FileContainingExtension:
   314  			typeName := req.FileContainingExtension.ContainingType
   315  			extNum := req.FileContainingExtension.ExtensionNumber
   316  			b, err := s.fileDescEncodingContainingExtension(typeName, extNum, sentFileDescriptors)
   317  			if err != nil {
   318  				out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{
   319  					ErrorResponse: &v1reflectionpb.ErrorResponse{
   320  						ErrorCode:    int32(codes.NotFound),
   321  						ErrorMessage: err.Error(),
   322  					},
   323  				}
   324  			} else {
   325  				out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_FileDescriptorResponse{
   326  					FileDescriptorResponse: &v1reflectionpb.FileDescriptorResponse{FileDescriptorProto: b},
   327  				}
   328  			}
   329  		case *v1reflectionpb.ServerReflectionRequest_AllExtensionNumbersOfType:
   330  			extNums, err := s.allExtensionNumbersForTypeName(req.AllExtensionNumbersOfType)
   331  			if err != nil {
   332  				out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{
   333  					ErrorResponse: &v1reflectionpb.ErrorResponse{
   334  						ErrorCode:    int32(codes.NotFound),
   335  						ErrorMessage: err.Error(),
   336  					},
   337  				}
   338  			} else {
   339  				out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_AllExtensionNumbersResponse{
   340  					AllExtensionNumbersResponse: &v1reflectionpb.ExtensionNumberResponse{
   341  						BaseTypeName:    req.AllExtensionNumbersOfType,
   342  						ExtensionNumber: extNums,
   343  					},
   344  				}
   345  			}
   346  		case *v1reflectionpb.ServerReflectionRequest_ListServices:
   347  			out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ListServicesResponse{
   348  				ListServicesResponse: &v1reflectionpb.ListServiceResponse{
   349  					Service: s.listServices(),
   350  				},
   351  			}
   352  		default:
   353  			return status.Errorf(codes.InvalidArgument, "invalid MessageRequest: %v", in.MessageRequest)
   354  		}
   355  
   356  		if err := stream.Send(out); err != nil {
   357  			return err
   358  		}
   359  	}
   360  }