mirror of https://github.com/grpc/grpc-go.git
				
				
				
			
		
			
				
	
	
		
			327 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			327 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
|  *
 | |
|  * 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
 | |
| 		}
 | |
| 	}
 | |
| }
 |