123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- /*
- *
- * Copyright 2016 gRPC authors.
- *
- * Licensed 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 reflection implements server reflection service.
- The service implemented is defined in:
- https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1alpha/reflection.proto.
- To register server reflection on a gRPC server:
- import "google.golang.org/grpc/reflection"
- s := grpc.NewServer()
- pb.RegisterYourOwnServer(s, &server{})
- // Register reflection service on gRPC server.
- reflection.Register(s)
- s.Serve(lis)
- */
- package reflection // import "google.golang.org/grpc/reflection"
- import (
- "io"
- "sort"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
- "google.golang.org/protobuf/proto"
- "google.golang.org/protobuf/reflect/protodesc"
- "google.golang.org/protobuf/reflect/protoreflect"
- "google.golang.org/protobuf/reflect/protoregistry"
- v1alphagrpc "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
- v1alphapb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
- )
- // GRPCServer is the interface provided by a gRPC server. It is implemented by
- // *grpc.Server, but could also be implemented by other concrete types. It acts
- // as a registry, for accumulating the services exposed by the server.
- type GRPCServer interface {
- grpc.ServiceRegistrar
- ServiceInfoProvider
- }
- var _ GRPCServer = (*grpc.Server)(nil)
- // Register registers the server reflection service on the given gRPC server.
- func Register(s GRPCServer) {
- svr := NewServer(ServerOptions{Services: s})
- v1alphagrpc.RegisterServerReflectionServer(s, svr)
- }
- // ServiceInfoProvider is an interface used to retrieve metadata about the
- // services to expose.
- //
- // The reflection service is only interested in the service names, but the
- // signature is this way so that *grpc.Server implements it. So it is okay
- // for a custom implementation to return zero values for the
- // grpc.ServiceInfo values in the map.
- //
- // # Experimental
- //
- // Notice: This type is EXPERIMENTAL and may be changed or removed in a
- // later release.
- type ServiceInfoProvider interface {
- GetServiceInfo() map[string]grpc.ServiceInfo
- }
- // ExtensionResolver is the interface used to query details about extensions.
- // This interface is satisfied by protoregistry.GlobalTypes.
- //
- // # Experimental
- //
- // Notice: This type is EXPERIMENTAL and may be changed or removed in a
- // later release.
- type ExtensionResolver interface {
- protoregistry.ExtensionTypeResolver
- RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool)
- }
- // ServerOptions represents the options used to construct a reflection server.
- //
- // # Experimental
- //
- // Notice: This type is EXPERIMENTAL and may be changed or removed in a
- // later release.
- type ServerOptions struct {
- // The source of advertised RPC services. If not specified, the reflection
- // server will report an empty list when asked to list services.
- //
- // This value will typically be a *grpc.Server. But the set of advertised
- // services can be customized by wrapping a *grpc.Server or using an
- // alternate implementation that returns a custom set of service names.
- Services ServiceInfoProvider
- // Optional resolver used to load descriptors. If not specified,
- // protoregistry.GlobalFiles will be used.
- DescriptorResolver protodesc.Resolver
- // Optional resolver used to query for known extensions. If not specified,
- // protoregistry.GlobalTypes will be used.
- ExtensionResolver ExtensionResolver
- }
- // NewServer returns a reflection server implementation using the given options.
- // This can be used to customize behavior of the reflection service. Most usages
- // should prefer to use Register instead.
- //
- // # Experimental
- //
- // Notice: This function is EXPERIMENTAL and may be changed or removed in a
- // later release.
- func NewServer(opts ServerOptions) v1alphagrpc.ServerReflectionServer {
- if opts.DescriptorResolver == nil {
- opts.DescriptorResolver = protoregistry.GlobalFiles
- }
- if opts.ExtensionResolver == nil {
- opts.ExtensionResolver = protoregistry.GlobalTypes
- }
- return &serverReflectionServer{
- s: opts.Services,
- descResolver: opts.DescriptorResolver,
- extResolver: opts.ExtensionResolver,
- }
- }
- type serverReflectionServer struct {
- v1alphagrpc.UnimplementedServerReflectionServer
- s ServiceInfoProvider
- descResolver protodesc.Resolver
- extResolver ExtensionResolver
- }
- // fileDescWithDependencies returns a slice of serialized fileDescriptors in
- // wire format ([]byte). The fileDescriptors will include fd and all the
- // transitive dependencies of fd with names not in sentFileDescriptors.
- func (s *serverReflectionServer) fileDescWithDependencies(fd protoreflect.FileDescriptor, sentFileDescriptors map[string]bool) ([][]byte, error) {
- var r [][]byte
- queue := []protoreflect.FileDescriptor{fd}
- for len(queue) > 0 {
- currentfd := queue[0]
- queue = queue[1:]
- if sent := sentFileDescriptors[currentfd.Path()]; len(r) == 0 || !sent {
- sentFileDescriptors[currentfd.Path()] = true
- fdProto := protodesc.ToFileDescriptorProto(currentfd)
- currentfdEncoded, err := proto.Marshal(fdProto)
- if err != nil {
- return nil, err
- }
- r = append(r, currentfdEncoded)
- }
- for i := 0; i < currentfd.Imports().Len(); i++ {
- queue = append(queue, currentfd.Imports().Get(i))
- }
- }
- return r, nil
- }
- // fileDescEncodingContainingSymbol finds the file descriptor containing the
- // given symbol, finds all of its previously unsent transitive dependencies,
- // does marshalling on them, and returns the marshalled result. The given symbol
- // can be a type, a service or a method.
- func (s *serverReflectionServer) fileDescEncodingContainingSymbol(name string, sentFileDescriptors map[string]bool) ([][]byte, error) {
- d, err := s.descResolver.FindDescriptorByName(protoreflect.FullName(name))
- if err != nil {
- return nil, err
- }
- return s.fileDescWithDependencies(d.ParentFile(), sentFileDescriptors)
- }
- // fileDescEncodingContainingExtension finds the file descriptor containing
- // given extension, finds all of its previously unsent transitive dependencies,
- // does marshalling on them, and returns the marshalled result.
- func (s *serverReflectionServer) fileDescEncodingContainingExtension(typeName string, extNum int32, sentFileDescriptors map[string]bool) ([][]byte, error) {
- xt, err := s.extResolver.FindExtensionByNumber(protoreflect.FullName(typeName), protoreflect.FieldNumber(extNum))
- if err != nil {
- return nil, err
- }
- return s.fileDescWithDependencies(xt.TypeDescriptor().ParentFile(), sentFileDescriptors)
- }
- // allExtensionNumbersForTypeName returns all extension numbers for the given type.
- func (s *serverReflectionServer) allExtensionNumbersForTypeName(name string) ([]int32, error) {
- var numbers []int32
- s.extResolver.RangeExtensionsByMessage(protoreflect.FullName(name), func(xt protoreflect.ExtensionType) bool {
- numbers = append(numbers, int32(xt.TypeDescriptor().Number()))
- return true
- })
- sort.Slice(numbers, func(i, j int) bool {
- return numbers[i] < numbers[j]
- })
- if len(numbers) == 0 {
- // maybe return an error if given type name is not known
- if _, err := s.descResolver.FindDescriptorByName(protoreflect.FullName(name)); err != nil {
- return nil, err
- }
- }
- return numbers, nil
- }
- // listServices returns the names of services this server exposes.
- func (s *serverReflectionServer) listServices() []*v1alphapb.ServiceResponse {
- serviceInfo := s.s.GetServiceInfo()
- resp := make([]*v1alphapb.ServiceResponse, 0, len(serviceInfo))
- for svc := range serviceInfo {
- resp = append(resp, &v1alphapb.ServiceResponse{Name: svc})
- }
- sort.Slice(resp, func(i, j int) bool {
- return resp[i].Name < resp[j].Name
- })
- return resp
- }
- // ServerReflectionInfo is the reflection service handler.
- func (s *serverReflectionServer) ServerReflectionInfo(stream v1alphagrpc.ServerReflection_ServerReflectionInfoServer) error {
- sentFileDescriptors := make(map[string]bool)
- for {
- in, err := stream.Recv()
- if err == io.EOF {
- return nil
- }
- if err != nil {
- return err
- }
- out := &v1alphapb.ServerReflectionResponse{
- ValidHost: in.Host,
- OriginalRequest: in,
- }
- switch req := in.MessageRequest.(type) {
- case *v1alphapb.ServerReflectionRequest_FileByFilename:
- var b [][]byte
- fd, err := s.descResolver.FindFileByPath(req.FileByFilename)
- if err == nil {
- b, err = s.fileDescWithDependencies(fd, sentFileDescriptors)
- }
- if err != nil {
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_ErrorResponse{
- ErrorResponse: &v1alphapb.ErrorResponse{
- ErrorCode: int32(codes.NotFound),
- ErrorMessage: err.Error(),
- },
- }
- } else {
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_FileDescriptorResponse{
- FileDescriptorResponse: &v1alphapb.FileDescriptorResponse{FileDescriptorProto: b},
- }
- }
- case *v1alphapb.ServerReflectionRequest_FileContainingSymbol:
- b, err := s.fileDescEncodingContainingSymbol(req.FileContainingSymbol, sentFileDescriptors)
- if err != nil {
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_ErrorResponse{
- ErrorResponse: &v1alphapb.ErrorResponse{
- ErrorCode: int32(codes.NotFound),
- ErrorMessage: err.Error(),
- },
- }
- } else {
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_FileDescriptorResponse{
- FileDescriptorResponse: &v1alphapb.FileDescriptorResponse{FileDescriptorProto: b},
- }
- }
- case *v1alphapb.ServerReflectionRequest_FileContainingExtension:
- typeName := req.FileContainingExtension.ContainingType
- extNum := req.FileContainingExtension.ExtensionNumber
- b, err := s.fileDescEncodingContainingExtension(typeName, extNum, sentFileDescriptors)
- if err != nil {
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_ErrorResponse{
- ErrorResponse: &v1alphapb.ErrorResponse{
- ErrorCode: int32(codes.NotFound),
- ErrorMessage: err.Error(),
- },
- }
- } else {
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_FileDescriptorResponse{
- FileDescriptorResponse: &v1alphapb.FileDescriptorResponse{FileDescriptorProto: b},
- }
- }
- case *v1alphapb.ServerReflectionRequest_AllExtensionNumbersOfType:
- extNums, err := s.allExtensionNumbersForTypeName(req.AllExtensionNumbersOfType)
- if err != nil {
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_ErrorResponse{
- ErrorResponse: &v1alphapb.ErrorResponse{
- ErrorCode: int32(codes.NotFound),
- ErrorMessage: err.Error(),
- },
- }
- } else {
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_AllExtensionNumbersResponse{
- AllExtensionNumbersResponse: &v1alphapb.ExtensionNumberResponse{
- BaseTypeName: req.AllExtensionNumbersOfType,
- ExtensionNumber: extNums,
- },
- }
- }
- case *v1alphapb.ServerReflectionRequest_ListServices:
- out.MessageResponse = &v1alphapb.ServerReflectionResponse_ListServicesResponse{
- ListServicesResponse: &v1alphapb.ListServiceResponse{
- Service: s.listServices(),
- },
- }
- default:
- return status.Errorf(codes.InvalidArgument, "invalid MessageRequest: %v", in.MessageRequest)
- }
- if err := stream.Send(out); err != nil {
- return err
- }
- }
- }
|