Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reflection: expose both v1 and v1alpha reflection services #6329

Merged
merged 3 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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