blob: e2f9ebfbbce85c9ee8d091a589b63a60ea9f538e [file] [log] [blame]
/*
*
* 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
}
}
}