| /* | |
| * | |
| * Copyright 2024 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 internal contains code that is shared by both reflection package and | |
| // the test package. The packages are split in this way inorder to avoid | |
| // dependency to deprecated package github.com/golang/protobuf. | |
| package internal | |
| 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" | |
| v1reflectiongrpc "google.golang.org/grpc/reflection/grpc_reflection_v1" | |
| v1reflectionpb "google.golang.org/grpc/reflection/grpc_reflection_v1" | |
| v1alphareflectiongrpc "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" | |
| v1alphareflectionpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" | |
| ) | |
| // ServiceInfoProvider is an interface used to retrieve metadata about the | |
| // services to expose. | |
| 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. | |
| type ExtensionResolver interface { | |
| protoregistry.ExtensionTypeResolver | |
| RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) | |
| } | |
| // ServerReflectionServer is the server API for ServerReflection service. | |
| type ServerReflectionServer struct { | |
| v1alphareflectiongrpc.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) { | |
| if fd.IsPlaceholder() { | |
| // If the given root file is a placeholder, treat it | |
| // as missing instead of serializing it. | |
| return nil, protoregistry.NotFound | |
| } | |
| var r [][]byte | |
| queue := []protoreflect.FileDescriptor{fd} | |
| for len(queue) > 0 { | |
| currentfd := queue[0] | |
| queue = queue[1:] | |
| if currentfd.IsPlaceholder() { | |
| // Skip any missing files in the dependency graph. | |
| continue | |
| } | |
| 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() []*v1reflectionpb.ServiceResponse { | |
| serviceInfo := s.S.GetServiceInfo() | |
| resp := make([]*v1reflectionpb.ServiceResponse, 0, len(serviceInfo)) | |
| for svc := range serviceInfo { | |
| resp = append(resp, &v1reflectionpb.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 v1reflectiongrpc.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 := &v1reflectionpb.ServerReflectionResponse{ | |
| ValidHost: in.Host, | |
| OriginalRequest: in, | |
| } | |
| switch req := in.MessageRequest.(type) { | |
| case *v1reflectionpb.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 = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{ | |
| ErrorResponse: &v1reflectionpb.ErrorResponse{ | |
| ErrorCode: int32(codes.NotFound), | |
| ErrorMessage: err.Error(), | |
| }, | |
| } | |
| } else { | |
| out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_FileDescriptorResponse{ | |
| FileDescriptorResponse: &v1reflectionpb.FileDescriptorResponse{FileDescriptorProto: b}, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionRequest_FileContainingSymbol: | |
| b, err := s.FileDescEncodingContainingSymbol(req.FileContainingSymbol, sentFileDescriptors) | |
| if err != nil { | |
| out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{ | |
| ErrorResponse: &v1reflectionpb.ErrorResponse{ | |
| ErrorCode: int32(codes.NotFound), | |
| ErrorMessage: err.Error(), | |
| }, | |
| } | |
| } else { | |
| out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_FileDescriptorResponse{ | |
| FileDescriptorResponse: &v1reflectionpb.FileDescriptorResponse{FileDescriptorProto: b}, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionRequest_FileContainingExtension: | |
| typeName := req.FileContainingExtension.ContainingType | |
| extNum := req.FileContainingExtension.ExtensionNumber | |
| b, err := s.FileDescEncodingContainingExtension(typeName, extNum, sentFileDescriptors) | |
| if err != nil { | |
| out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{ | |
| ErrorResponse: &v1reflectionpb.ErrorResponse{ | |
| ErrorCode: int32(codes.NotFound), | |
| ErrorMessage: err.Error(), | |
| }, | |
| } | |
| } else { | |
| out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_FileDescriptorResponse{ | |
| FileDescriptorResponse: &v1reflectionpb.FileDescriptorResponse{FileDescriptorProto: b}, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionRequest_AllExtensionNumbersOfType: | |
| extNums, err := s.AllExtensionNumbersForTypeName(req.AllExtensionNumbersOfType) | |
| if err != nil { | |
| out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{ | |
| ErrorResponse: &v1reflectionpb.ErrorResponse{ | |
| ErrorCode: int32(codes.NotFound), | |
| ErrorMessage: err.Error(), | |
| }, | |
| } | |
| } else { | |
| out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_AllExtensionNumbersResponse{ | |
| AllExtensionNumbersResponse: &v1reflectionpb.ExtensionNumberResponse{ | |
| BaseTypeName: req.AllExtensionNumbersOfType, | |
| ExtensionNumber: extNums, | |
| }, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionRequest_ListServices: | |
| out.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ListServicesResponse{ | |
| ListServicesResponse: &v1reflectionpb.ListServiceResponse{ | |
| Service: s.ListServices(), | |
| }, | |
| } | |
| default: | |
| return status.Errorf(codes.InvalidArgument, "invalid MessageRequest: %v", in.MessageRequest) | |
| } | |
| if err := stream.Send(out); err != nil { | |
| return err | |
| } | |
| } | |
| } | |
| // V1ToV1AlphaResponse converts a v1 ServerReflectionResponse to a v1alpha. | |
| func V1ToV1AlphaResponse(v1 *v1reflectionpb.ServerReflectionResponse) *v1alphareflectionpb.ServerReflectionResponse { | |
| var v1alpha v1alphareflectionpb.ServerReflectionResponse | |
| v1alpha.ValidHost = v1.ValidHost | |
| if v1.OriginalRequest != nil { | |
| v1alpha.OriginalRequest = V1ToV1AlphaRequest(v1.OriginalRequest) | |
| } | |
| switch mr := v1.MessageResponse.(type) { | |
| case *v1reflectionpb.ServerReflectionResponse_FileDescriptorResponse: | |
| if mr != nil { | |
| v1alpha.MessageResponse = &v1alphareflectionpb.ServerReflectionResponse_FileDescriptorResponse{ | |
| FileDescriptorResponse: &v1alphareflectionpb.FileDescriptorResponse{ | |
| FileDescriptorProto: mr.FileDescriptorResponse.GetFileDescriptorProto(), | |
| }, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionResponse_AllExtensionNumbersResponse: | |
| if mr != nil { | |
| v1alpha.MessageResponse = &v1alphareflectionpb.ServerReflectionResponse_AllExtensionNumbersResponse{ | |
| AllExtensionNumbersResponse: &v1alphareflectionpb.ExtensionNumberResponse{ | |
| BaseTypeName: mr.AllExtensionNumbersResponse.GetBaseTypeName(), | |
| ExtensionNumber: mr.AllExtensionNumbersResponse.GetExtensionNumber(), | |
| }, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionResponse_ListServicesResponse: | |
| if mr != nil { | |
| svcs := make([]*v1alphareflectionpb.ServiceResponse, len(mr.ListServicesResponse.GetService())) | |
| for i, svc := range mr.ListServicesResponse.GetService() { | |
| svcs[i] = &v1alphareflectionpb.ServiceResponse{ | |
| Name: svc.GetName(), | |
| } | |
| } | |
| v1alpha.MessageResponse = &v1alphareflectionpb.ServerReflectionResponse_ListServicesResponse{ | |
| ListServicesResponse: &v1alphareflectionpb.ListServiceResponse{ | |
| Service: svcs, | |
| }, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionResponse_ErrorResponse: | |
| if mr != nil { | |
| v1alpha.MessageResponse = &v1alphareflectionpb.ServerReflectionResponse_ErrorResponse{ | |
| ErrorResponse: &v1alphareflectionpb.ErrorResponse{ | |
| ErrorCode: mr.ErrorResponse.GetErrorCode(), | |
| ErrorMessage: mr.ErrorResponse.GetErrorMessage(), | |
| }, | |
| } | |
| } | |
| default: | |
| // no value set | |
| } | |
| return &v1alpha | |
| } | |
| // V1AlphaToV1Request converts a v1alpha ServerReflectionRequest to a v1. | |
| func V1AlphaToV1Request(v1alpha *v1alphareflectionpb.ServerReflectionRequest) *v1reflectionpb.ServerReflectionRequest { | |
| var v1 v1reflectionpb.ServerReflectionRequest | |
| v1.Host = v1alpha.Host | |
| switch mr := v1alpha.MessageRequest.(type) { | |
| case *v1alphareflectionpb.ServerReflectionRequest_FileByFilename: | |
| v1.MessageRequest = &v1reflectionpb.ServerReflectionRequest_FileByFilename{ | |
| FileByFilename: mr.FileByFilename, | |
| } | |
| case *v1alphareflectionpb.ServerReflectionRequest_FileContainingSymbol: | |
| v1.MessageRequest = &v1reflectionpb.ServerReflectionRequest_FileContainingSymbol{ | |
| FileContainingSymbol: mr.FileContainingSymbol, | |
| } | |
| case *v1alphareflectionpb.ServerReflectionRequest_FileContainingExtension: | |
| if mr.FileContainingExtension != nil { | |
| v1.MessageRequest = &v1reflectionpb.ServerReflectionRequest_FileContainingExtension{ | |
| FileContainingExtension: &v1reflectionpb.ExtensionRequest{ | |
| ContainingType: mr.FileContainingExtension.GetContainingType(), | |
| ExtensionNumber: mr.FileContainingExtension.GetExtensionNumber(), | |
| }, | |
| } | |
| } | |
| case *v1alphareflectionpb.ServerReflectionRequest_AllExtensionNumbersOfType: | |
| v1.MessageRequest = &v1reflectionpb.ServerReflectionRequest_AllExtensionNumbersOfType{ | |
| AllExtensionNumbersOfType: mr.AllExtensionNumbersOfType, | |
| } | |
| case *v1alphareflectionpb.ServerReflectionRequest_ListServices: | |
| v1.MessageRequest = &v1reflectionpb.ServerReflectionRequest_ListServices{ | |
| ListServices: mr.ListServices, | |
| } | |
| default: | |
| // no value set | |
| } | |
| return &v1 | |
| } | |
| // V1ToV1AlphaRequest converts a v1 ServerReflectionRequest to a v1alpha. | |
| func V1ToV1AlphaRequest(v1 *v1reflectionpb.ServerReflectionRequest) *v1alphareflectionpb.ServerReflectionRequest { | |
| var v1alpha v1alphareflectionpb.ServerReflectionRequest | |
| v1alpha.Host = v1.Host | |
| switch mr := v1.MessageRequest.(type) { | |
| case *v1reflectionpb.ServerReflectionRequest_FileByFilename: | |
| if mr != nil { | |
| v1alpha.MessageRequest = &v1alphareflectionpb.ServerReflectionRequest_FileByFilename{ | |
| FileByFilename: mr.FileByFilename, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionRequest_FileContainingSymbol: | |
| if mr != nil { | |
| v1alpha.MessageRequest = &v1alphareflectionpb.ServerReflectionRequest_FileContainingSymbol{ | |
| FileContainingSymbol: mr.FileContainingSymbol, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionRequest_FileContainingExtension: | |
| if mr != nil { | |
| v1alpha.MessageRequest = &v1alphareflectionpb.ServerReflectionRequest_FileContainingExtension{ | |
| FileContainingExtension: &v1alphareflectionpb.ExtensionRequest{ | |
| ContainingType: mr.FileContainingExtension.GetContainingType(), | |
| ExtensionNumber: mr.FileContainingExtension.GetExtensionNumber(), | |
| }, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionRequest_AllExtensionNumbersOfType: | |
| if mr != nil { | |
| v1alpha.MessageRequest = &v1alphareflectionpb.ServerReflectionRequest_AllExtensionNumbersOfType{ | |
| AllExtensionNumbersOfType: mr.AllExtensionNumbersOfType, | |
| } | |
| } | |
| case *v1reflectionpb.ServerReflectionRequest_ListServices: | |
| if mr != nil { | |
| v1alpha.MessageRequest = &v1alphareflectionpb.ServerReflectionRequest_ListServices{ | |
| ListServices: mr.ListServices, | |
| } | |
| } | |
| default: | |
| // no value set | |
| } | |
| return &v1alpha | |
| } | |
| // V1AlphaToV1Response converts a v1alpha ServerReflectionResponse to a v1. | |
| func V1AlphaToV1Response(v1alpha *v1alphareflectionpb.ServerReflectionResponse) *v1reflectionpb.ServerReflectionResponse { | |
| var v1 v1reflectionpb.ServerReflectionResponse | |
| v1.ValidHost = v1alpha.ValidHost | |
| if v1alpha.OriginalRequest != nil { | |
| v1.OriginalRequest = V1AlphaToV1Request(v1alpha.OriginalRequest) | |
| } | |
| switch mr := v1alpha.MessageResponse.(type) { | |
| case *v1alphareflectionpb.ServerReflectionResponse_FileDescriptorResponse: | |
| if mr != nil { | |
| v1.MessageResponse = &v1reflectionpb.ServerReflectionResponse_FileDescriptorResponse{ | |
| FileDescriptorResponse: &v1reflectionpb.FileDescriptorResponse{ | |
| FileDescriptorProto: mr.FileDescriptorResponse.GetFileDescriptorProto(), | |
| }, | |
| } | |
| } | |
| case *v1alphareflectionpb.ServerReflectionResponse_AllExtensionNumbersResponse: | |
| if mr != nil { | |
| v1.MessageResponse = &v1reflectionpb.ServerReflectionResponse_AllExtensionNumbersResponse{ | |
| AllExtensionNumbersResponse: &v1reflectionpb.ExtensionNumberResponse{ | |
| BaseTypeName: mr.AllExtensionNumbersResponse.GetBaseTypeName(), | |
| ExtensionNumber: mr.AllExtensionNumbersResponse.GetExtensionNumber(), | |
| }, | |
| } | |
| } | |
| case *v1alphareflectionpb.ServerReflectionResponse_ListServicesResponse: | |
| if mr != nil { | |
| svcs := make([]*v1reflectionpb.ServiceResponse, len(mr.ListServicesResponse.GetService())) | |
| for i, svc := range mr.ListServicesResponse.GetService() { | |
| svcs[i] = &v1reflectionpb.ServiceResponse{ | |
| Name: svc.GetName(), | |
| } | |
| } | |
| v1.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ListServicesResponse{ | |
| ListServicesResponse: &v1reflectionpb.ListServiceResponse{ | |
| Service: svcs, | |
| }, | |
| } | |
| } | |
| case *v1alphareflectionpb.ServerReflectionResponse_ErrorResponse: | |
| if mr != nil { | |
| v1.MessageResponse = &v1reflectionpb.ServerReflectionResponse_ErrorResponse{ | |
| ErrorResponse: &v1reflectionpb.ErrorResponse{ | |
| ErrorCode: mr.ErrorResponse.GetErrorCode(), | |
| ErrorMessage: mr.ErrorResponse.GetErrorMessage(), | |
| }, | |
| } | |
| } | |
| default: | |
| // no value set | |
| } | |
| return &v1 | |
| } |