Skip to content

Commit

Permalink
reflection: expose both v1 and v1alpha reflection services (#6329)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhump authored Jun 12, 2023
1 parent 3c6084b commit 642dd63
Show file tree
Hide file tree
Showing 3 changed files with 411 additions and 100 deletions.
187 changes: 187 additions & 0 deletions reflection/adapt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
*
* Copyright 2023 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

import (
v1grpc "google.golang.org/grpc/reflection/grpc_reflection_v1"
v1pb "google.golang.org/grpc/reflection/grpc_reflection_v1"
v1alphagrpc "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
v1alphapb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
)

// asV1Alpha returns an implementation of the v1alpha version of the reflection
// interface that delegates all calls to the given v1 version.
func asV1Alpha(svr v1grpc.ServerReflectionServer) v1alphagrpc.ServerReflectionServer {
return v1AlphaServerImpl{svr: svr}
}

type v1AlphaServerImpl struct {
svr v1grpc.ServerReflectionServer
}

func (s v1AlphaServerImpl) ServerReflectionInfo(stream v1alphagrpc.ServerReflection_ServerReflectionInfoServer) error {
return s.svr.ServerReflectionInfo(v1AlphaServerStreamAdapter{stream})
}

type v1AlphaServerStreamAdapter struct {
v1alphagrpc.ServerReflection_ServerReflectionInfoServer
}

func (s v1AlphaServerStreamAdapter) Send(response *v1pb.ServerReflectionResponse) error {
return s.ServerReflection_ServerReflectionInfoServer.Send(v1ToV1AlphaResponse(response))
}

func (s v1AlphaServerStreamAdapter) Recv() (*v1pb.ServerReflectionRequest, error) {
resp, err := s.ServerReflection_ServerReflectionInfoServer.Recv()
if err != nil {
return nil, err
}
return v1AlphaToV1Request(resp), nil
}

func v1ToV1AlphaResponse(v1 *v1pb.ServerReflectionResponse) *v1alphapb.ServerReflectionResponse {
var v1alpha v1alphapb.ServerReflectionResponse
v1alpha.ValidHost = v1.ValidHost
if v1.OriginalRequest != nil {
v1alpha.OriginalRequest = v1ToV1AlphaRequest(v1.OriginalRequest)
}
switch mr := v1.MessageResponse.(type) {
case *v1pb.ServerReflectionResponse_FileDescriptorResponse:
if mr != nil {
v1alpha.MessageResponse = &v1alphapb.ServerReflectionResponse_FileDescriptorResponse{
FileDescriptorResponse: &v1alphapb.FileDescriptorResponse{
FileDescriptorProto: mr.FileDescriptorResponse.GetFileDescriptorProto(),
},
}
}
case *v1pb.ServerReflectionResponse_AllExtensionNumbersResponse:
if mr != nil {
v1alpha.MessageResponse = &v1alphapb.ServerReflectionResponse_AllExtensionNumbersResponse{
AllExtensionNumbersResponse: &v1alphapb.ExtensionNumberResponse{
BaseTypeName: mr.AllExtensionNumbersResponse.GetBaseTypeName(),
ExtensionNumber: mr.AllExtensionNumbersResponse.GetExtensionNumber(),
},
}
}
case *v1pb.ServerReflectionResponse_ListServicesResponse:
if mr != nil {
svcs := make([]*v1alphapb.ServiceResponse, len(mr.ListServicesResponse.GetService()))
for i, svc := range mr.ListServicesResponse.GetService() {
svcs[i] = &v1alphapb.ServiceResponse{
Name: svc.GetName(),
}
}
v1alpha.MessageResponse = &v1alphapb.ServerReflectionResponse_ListServicesResponse{
ListServicesResponse: &v1alphapb.ListServiceResponse{
Service: svcs,
},
}
}
case *v1pb.ServerReflectionResponse_ErrorResponse:
if mr != nil {
v1alpha.MessageResponse = &v1alphapb.ServerReflectionResponse_ErrorResponse{
ErrorResponse: &v1alphapb.ErrorResponse{
ErrorCode: mr.ErrorResponse.GetErrorCode(),
ErrorMessage: mr.ErrorResponse.GetErrorMessage(),
},
}
}
default:
// no value set
}
return &v1alpha
}

func v1AlphaToV1Request(v1alpha *v1alphapb.ServerReflectionRequest) *v1pb.ServerReflectionRequest {
var v1 v1pb.ServerReflectionRequest
v1.Host = v1alpha.Host
switch mr := v1alpha.MessageRequest.(type) {
case *v1alphapb.ServerReflectionRequest_FileByFilename:
v1.MessageRequest = &v1pb.ServerReflectionRequest_FileByFilename{
FileByFilename: mr.FileByFilename,
}
case *v1alphapb.ServerReflectionRequest_FileContainingSymbol:
v1.MessageRequest = &v1pb.ServerReflectionRequest_FileContainingSymbol{
FileContainingSymbol: mr.FileContainingSymbol,
}
case *v1alphapb.ServerReflectionRequest_FileContainingExtension:
if mr.FileContainingExtension != nil {
v1.MessageRequest = &v1pb.ServerReflectionRequest_FileContainingExtension{
FileContainingExtension: &v1pb.ExtensionRequest{
ContainingType: mr.FileContainingExtension.GetContainingType(),
ExtensionNumber: mr.FileContainingExtension.GetExtensionNumber(),
},
}
}
case *v1alphapb.ServerReflectionRequest_AllExtensionNumbersOfType:
v1.MessageRequest = &v1pb.ServerReflectionRequest_AllExtensionNumbersOfType{
AllExtensionNumbersOfType: mr.AllExtensionNumbersOfType,
}
case *v1alphapb.ServerReflectionRequest_ListServices:
v1.MessageRequest = &v1pb.ServerReflectionRequest_ListServices{
ListServices: mr.ListServices,
}
default:
// no value set
}
return &v1
}

func v1ToV1AlphaRequest(v1 *v1pb.ServerReflectionRequest) *v1alphapb.ServerReflectionRequest {
var v1alpha v1alphapb.ServerReflectionRequest
v1alpha.Host = v1.Host
switch mr := v1.MessageRequest.(type) {
case *v1pb.ServerReflectionRequest_FileByFilename:
if mr != nil {
v1alpha.MessageRequest = &v1alphapb.ServerReflectionRequest_FileByFilename{
FileByFilename: mr.FileByFilename,
}
}
case *v1pb.ServerReflectionRequest_FileContainingSymbol:
if mr != nil {
v1alpha.MessageRequest = &v1alphapb.ServerReflectionRequest_FileContainingSymbol{
FileContainingSymbol: mr.FileContainingSymbol,
}
}
case *v1pb.ServerReflectionRequest_FileContainingExtension:
if mr != nil {
v1alpha.MessageRequest = &v1alphapb.ServerReflectionRequest_FileContainingExtension{
FileContainingExtension: &v1alphapb.ExtensionRequest{
ContainingType: mr.FileContainingExtension.GetContainingType(),
ExtensionNumber: mr.FileContainingExtension.GetExtensionNumber(),
},
}
}
case *v1pb.ServerReflectionRequest_AllExtensionNumbersOfType:
if mr != nil {
v1alpha.MessageRequest = &v1alphapb.ServerReflectionRequest_AllExtensionNumbersOfType{
AllExtensionNumbersOfType: mr.AllExtensionNumbersOfType,
}
}
case *v1pb.ServerReflectionRequest_ListServices:
if mr != nil {
v1alpha.MessageRequest = &v1alphapb.ServerReflectionRequest_ListServices{
ListServices: mr.ListServices,
}
}
default:
// no value set
}
return &v1alpha
}
89 changes: 57 additions & 32 deletions reflection/serverreflection.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ import (
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"

v1grpc "google.golang.org/grpc/reflection/grpc_reflection_v1"
v1pb "google.golang.org/grpc/reflection/grpc_reflection_v1"
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
Expand All @@ -63,9 +64,19 @@ type GRPCServer interface {
var _ GRPCServer = (*grpc.Server)(nil)

// Register registers the server reflection service on the given gRPC server.
// Both the v1 and v1alpha versions are registered.
func Register(s GRPCServer) {
svr := NewServer(ServerOptions{Services: s})
v1alphagrpc.RegisterServerReflectionServer(s, svr)
svr := NewServerV1(ServerOptions{Services: s})
v1alphagrpc.RegisterServerReflectionServer(s, asV1Alpha(svr))
v1grpc.RegisterServerReflectionServer(s, svr)
}

// RegisterV1 registers only the v1 version of the server reflection service
// on the given gRPC server. Many clients may only support v1alpha so most
// users should use Register instead, at least until clients have upgraded.
func RegisterV1(s GRPCServer) {
svr := NewServerV1(ServerOptions{Services: s})
v1grpc.RegisterServerReflectionServer(s, svr)
}

// ServiceInfoProvider is an interface used to retrieve metadata about the
Expand Down Expand Up @@ -120,13 +131,27 @@ type ServerOptions struct {

// 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.
// should prefer to use Register instead. For backwards compatibility reasons,
// this returns the v1alpha version of the reflection server. For a v1 version
// of the reflection server, see NewServerV1.
//
// # Experimental
//
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
// later release.
func NewServer(opts ServerOptions) v1alphagrpc.ServerReflectionServer {
return asV1Alpha(NewServerV1(opts))
}

// NewServerV1 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 NewServerV1(opts ServerOptions) v1grpc.ServerReflectionServer {
if opts.DescriptorResolver == nil {
opts.DescriptorResolver = protoregistry.GlobalFiles
}
Expand Down Expand Up @@ -215,11 +240,11 @@ func (s *serverReflectionServer) allExtensionNumbersForTypeName(name string) ([]
}

// listServices returns the names of services this server exposes.
func (s *serverReflectionServer) listServices() []*v1alphapb.ServiceResponse {
func (s *serverReflectionServer) listServices() []*v1pb.ServiceResponse {
serviceInfo := s.s.GetServiceInfo()
resp := make([]*v1alphapb.ServiceResponse, 0, len(serviceInfo))
resp := make([]*v1pb.ServiceResponse, 0, len(serviceInfo))
for svc := range serviceInfo {
resp = append(resp, &v1alphapb.ServiceResponse{Name: svc})
resp = append(resp, &v1pb.ServiceResponse{Name: svc})
}
sort.Slice(resp, func(i, j int) bool {
return resp[i].Name < resp[j].Name
Expand All @@ -228,7 +253,7 @@ func (s *serverReflectionServer) listServices() []*v1alphapb.ServiceResponse {
}

// ServerReflectionInfo is the reflection service handler.
func (s *serverReflectionServer) ServerReflectionInfo(stream v1alphagrpc.ServerReflection_ServerReflectionInfoServer) error {
func (s *serverReflectionServer) ServerReflectionInfo(stream v1grpc.ServerReflection_ServerReflectionInfoServer) error {
sentFileDescriptors := make(map[string]bool)
for {
in, err := stream.Recv()
Expand All @@ -239,79 +264,79 @@ func (s *serverReflectionServer) ServerReflectionInfo(stream v1alphagrpc.ServerR
return err
}

out := &v1alphapb.ServerReflectionResponse{
out := &v1pb.ServerReflectionResponse{
ValidHost: in.Host,
OriginalRequest: in,
}
switch req := in.MessageRequest.(type) {
case *v1alphapb.ServerReflectionRequest_FileByFilename:
case *v1pb.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{
out.MessageResponse = &v1pb.ServerReflectionResponse_ErrorResponse{
ErrorResponse: &v1pb.ErrorResponse{
ErrorCode: int32(codes.NotFound),
ErrorMessage: err.Error(),
},
}
} else {
out.MessageResponse = &v1alphapb.ServerReflectionResponse_FileDescriptorResponse{
FileDescriptorResponse: &v1alphapb.FileDescriptorResponse{FileDescriptorProto: b},
out.MessageResponse = &v1pb.ServerReflectionResponse_FileDescriptorResponse{
FileDescriptorResponse: &v1pb.FileDescriptorResponse{FileDescriptorProto: b},
}
}
case *v1alphapb.ServerReflectionRequest_FileContainingSymbol:
case *v1pb.ServerReflectionRequest_FileContainingSymbol:
b, err := s.fileDescEncodingContainingSymbol(req.FileContainingSymbol, sentFileDescriptors)
if err != nil {
out.MessageResponse = &v1alphapb.ServerReflectionResponse_ErrorResponse{
ErrorResponse: &v1alphapb.ErrorResponse{
out.MessageResponse = &v1pb.ServerReflectionResponse_ErrorResponse{
ErrorResponse: &v1pb.ErrorResponse{
ErrorCode: int32(codes.NotFound),
ErrorMessage: err.Error(),
},
}
} else {
out.MessageResponse = &v1alphapb.ServerReflectionResponse_FileDescriptorResponse{
FileDescriptorResponse: &v1alphapb.FileDescriptorResponse{FileDescriptorProto: b},
out.MessageResponse = &v1pb.ServerReflectionResponse_FileDescriptorResponse{
FileDescriptorResponse: &v1pb.FileDescriptorResponse{FileDescriptorProto: b},
}
}
case *v1alphapb.ServerReflectionRequest_FileContainingExtension:
case *v1pb.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{
out.MessageResponse = &v1pb.ServerReflectionResponse_ErrorResponse{
ErrorResponse: &v1pb.ErrorResponse{
ErrorCode: int32(codes.NotFound),
ErrorMessage: err.Error(),
},
}
} else {
out.MessageResponse = &v1alphapb.ServerReflectionResponse_FileDescriptorResponse{
FileDescriptorResponse: &v1alphapb.FileDescriptorResponse{FileDescriptorProto: b},
out.MessageResponse = &v1pb.ServerReflectionResponse_FileDescriptorResponse{
FileDescriptorResponse: &v1pb.FileDescriptorResponse{FileDescriptorProto: b},
}
}
case *v1alphapb.ServerReflectionRequest_AllExtensionNumbersOfType:
case *v1pb.ServerReflectionRequest_AllExtensionNumbersOfType:
extNums, err := s.allExtensionNumbersForTypeName(req.AllExtensionNumbersOfType)
if err != nil {
out.MessageResponse = &v1alphapb.ServerReflectionResponse_ErrorResponse{
ErrorResponse: &v1alphapb.ErrorResponse{
out.MessageResponse = &v1pb.ServerReflectionResponse_ErrorResponse{
ErrorResponse: &v1pb.ErrorResponse{
ErrorCode: int32(codes.NotFound),
ErrorMessage: err.Error(),
},
}
} else {
out.MessageResponse = &v1alphapb.ServerReflectionResponse_AllExtensionNumbersResponse{
AllExtensionNumbersResponse: &v1alphapb.ExtensionNumberResponse{
out.MessageResponse = &v1pb.ServerReflectionResponse_AllExtensionNumbersResponse{
AllExtensionNumbersResponse: &v1pb.ExtensionNumberResponse{
BaseTypeName: req.AllExtensionNumbersOfType,
ExtensionNumber: extNums,
},
}
}
case *v1alphapb.ServerReflectionRequest_ListServices:
out.MessageResponse = &v1alphapb.ServerReflectionResponse_ListServicesResponse{
ListServicesResponse: &v1alphapb.ListServiceResponse{
case *v1pb.ServerReflectionRequest_ListServices:
out.MessageResponse = &v1pb.ServerReflectionResponse_ListServicesResponse{
ListServicesResponse: &v1pb.ListServiceResponse{
Service: s.listServices(),
},
}
Expand Down
Loading

0 comments on commit 642dd63

Please sign in to comment.