mirror of https://github.com/grpc/grpc-go.git
reflection: don't serialize placeholders (#6771)
This commit is contained in:
parent
4a84ce61ec
commit
3cbbe2947f
|
@ -176,11 +176,20 @@ type serverReflectionServer struct {
|
||||||
// wire format ([]byte). The fileDescriptors will include fd and all the
|
// wire format ([]byte). The fileDescriptors will include fd and all the
|
||||||
// transitive dependencies of fd with names not in sentFileDescriptors.
|
// transitive dependencies of fd with names not in sentFileDescriptors.
|
||||||
func (s *serverReflectionServer) fileDescWithDependencies(fd protoreflect.FileDescriptor, sentFileDescriptors map[string]bool) ([][]byte, error) {
|
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
|
var r [][]byte
|
||||||
queue := []protoreflect.FileDescriptor{fd}
|
queue := []protoreflect.FileDescriptor{fd}
|
||||||
for len(queue) > 0 {
|
for len(queue) > 0 {
|
||||||
currentfd := queue[0]
|
currentfd := queue[0]
|
||||||
queue = queue[1:]
|
queue = queue[1:]
|
||||||
|
if currentfd.IsPlaceholder() {
|
||||||
|
// Skip any missing files in the dependency graph.
|
||||||
|
continue
|
||||||
|
}
|
||||||
if sent := sentFileDescriptors[currentfd.Path()]; len(r) == 0 || !sent {
|
if sent := sentFileDescriptors[currentfd.Path()]; len(r) == 0 || !sent {
|
||||||
sentFileDescriptors[currentfd.Path()] = true
|
sentFileDescriptors[currentfd.Path()] = true
|
||||||
fdProto := protodesc.ToFileDescriptorProto(currentfd)
|
fdProto := protodesc.ToFileDescriptorProto(currentfd)
|
||||||
|
|
|
@ -170,6 +170,206 @@ func (x) TestAllExtensionNumbersForTypeName(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x) TestFileDescWithDependencies(t *testing.T) {
|
||||||
|
depFile, err := protodesc.NewFile(
|
||||||
|
&descriptorpb.FileDescriptorProto{
|
||||||
|
Name: proto.String("dep.proto"),
|
||||||
|
}, nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deps := &protoregistry.Files{}
|
||||||
|
if err := deps.RegisterFile(depFile); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootFileProto := &descriptorpb.FileDescriptorProto{
|
||||||
|
Name: proto.String("root.proto"),
|
||||||
|
Dependency: []string{
|
||||||
|
"google/protobuf/descriptor.proto",
|
||||||
|
"reflection/grpc_testing/proto2_ext2.proto",
|
||||||
|
"dep.proto",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// dep.proto is in deps; the other imports come from protoregistry.GlobalFiles
|
||||||
|
resolver := &combinedResolver{first: protoregistry.GlobalFiles, second: deps}
|
||||||
|
rootFile, err := protodesc.NewFile(rootFileProto, resolver)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a file hierarchy that contains a placeholder for dep.proto
|
||||||
|
placeholderDep := placeholderFile{depFile}
|
||||||
|
placeholderDeps := &protoregistry.Files{}
|
||||||
|
if err := placeholderDeps.RegisterFile(placeholderDep); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
resolver = &combinedResolver{first: protoregistry.GlobalFiles, second: placeholderDeps}
|
||||||
|
|
||||||
|
rootFileHasPlaceholderDep, err := protodesc.NewFile(rootFileProto, resolver)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootFileIsPlaceholder := placeholderFile{rootFile}
|
||||||
|
|
||||||
|
// Full transitive dependency graph of root.proto includes five files:
|
||||||
|
// - root.proto
|
||||||
|
// - google/protobuf/descriptor.proto
|
||||||
|
// - reflection/grpc_testing/proto2_ext2.proto
|
||||||
|
// - reflection/grpc_testing/proto2.proto
|
||||||
|
// - dep.proto
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
|
sent []string
|
||||||
|
root protoreflect.FileDescriptor
|
||||||
|
expect []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "send_all",
|
||||||
|
root: rootFile,
|
||||||
|
// expect full transitive closure
|
||||||
|
expect: []string{
|
||||||
|
"root.proto",
|
||||||
|
"google/protobuf/descriptor.proto",
|
||||||
|
"reflection/grpc_testing/proto2_ext2.proto",
|
||||||
|
"reflection/grpc_testing/proto2.proto",
|
||||||
|
"dep.proto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "already_sent",
|
||||||
|
sent: []string{
|
||||||
|
"root.proto",
|
||||||
|
"google/protobuf/descriptor.proto",
|
||||||
|
"reflection/grpc_testing/proto2_ext2.proto",
|
||||||
|
"reflection/grpc_testing/proto2.proto",
|
||||||
|
"dep.proto",
|
||||||
|
},
|
||||||
|
root: rootFile,
|
||||||
|
// expect only the root to be re-sent
|
||||||
|
expect: []string{"root.proto"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "some_already_sent",
|
||||||
|
sent: []string{
|
||||||
|
"reflection/grpc_testing/proto2_ext2.proto",
|
||||||
|
"reflection/grpc_testing/proto2.proto",
|
||||||
|
},
|
||||||
|
root: rootFile,
|
||||||
|
expect: []string{
|
||||||
|
"root.proto",
|
||||||
|
"google/protobuf/descriptor.proto",
|
||||||
|
"dep.proto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "root_is_placeholder",
|
||||||
|
root: rootFileIsPlaceholder,
|
||||||
|
// expect error, no files
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "placeholder_skipped",
|
||||||
|
root: rootFileHasPlaceholderDep,
|
||||||
|
// dep.proto is a placeholder so is skipped
|
||||||
|
expect: []string{
|
||||||
|
"root.proto",
|
||||||
|
"google/protobuf/descriptor.proto",
|
||||||
|
"reflection/grpc_testing/proto2_ext2.proto",
|
||||||
|
"reflection/grpc_testing/proto2.proto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "placeholder_skipped_and_some_sent",
|
||||||
|
sent: []string{
|
||||||
|
"reflection/grpc_testing/proto2_ext2.proto",
|
||||||
|
"reflection/grpc_testing/proto2.proto",
|
||||||
|
},
|
||||||
|
root: rootFileHasPlaceholderDep,
|
||||||
|
expect: []string{
|
||||||
|
"root.proto",
|
||||||
|
"google/protobuf/descriptor.proto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
s := NewServerV1(ServerOptions{}).(*serverReflectionServer)
|
||||||
|
|
||||||
|
sent := map[string]bool{}
|
||||||
|
for _, path := range test.sent {
|
||||||
|
sent[path] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptors, err := s.fileDescWithDependencies(test.root, sent)
|
||||||
|
if len(test.expect) == 0 {
|
||||||
|
// if we're not expecting any files then we're expecting an error
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expecting an error; instead got %d files", len(descriptors))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDescriptorResults(t, descriptors, test.expect)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDescriptorResults(t *testing.T, descriptors [][]byte, expect []string) {
|
||||||
|
t.Helper()
|
||||||
|
if len(descriptors) != len(expect) {
|
||||||
|
t.Errorf("expected result to contain %d descriptor(s); instead got %d", len(expect), len(descriptors))
|
||||||
|
}
|
||||||
|
names := map[string]struct{}{}
|
||||||
|
for i, desc := range descriptors {
|
||||||
|
var descProto descriptorpb.FileDescriptorProto
|
||||||
|
if err := proto.Unmarshal(desc, &descProto); err != nil {
|
||||||
|
t.Fatalf("could not unmarshal descriptor result #%d", i+1)
|
||||||
|
}
|
||||||
|
names[descProto.GetName()] = struct{}{}
|
||||||
|
}
|
||||||
|
actual := make([]string, 0, len(names))
|
||||||
|
for name := range names {
|
||||||
|
actual = append(actual, name)
|
||||||
|
}
|
||||||
|
sort.Strings(actual)
|
||||||
|
sort.Strings(expect)
|
||||||
|
if !reflect.DeepEqual(actual, expect) {
|
||||||
|
t.Fatalf("expected file descriptors for %v; instead got %v", expect, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type placeholderFile struct {
|
||||||
|
protoreflect.FileDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (placeholderFile) IsPlaceholder() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type combinedResolver struct {
|
||||||
|
first, second protodesc.Resolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *combinedResolver) FindFileByPath(path string) (protoreflect.FileDescriptor, error) {
|
||||||
|
file, err := r.first.FindFileByPath(path)
|
||||||
|
if err == nil {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
return r.second.FindFileByPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *combinedResolver) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) {
|
||||||
|
desc, err := r.first.FindDescriptorByName(name)
|
||||||
|
if err == nil {
|
||||||
|
return desc, nil
|
||||||
|
}
|
||||||
|
return r.second.FindDescriptorByName(name)
|
||||||
|
}
|
||||||
|
|
||||||
// Do end2end tests.
|
// Do end2end tests.
|
||||||
|
|
||||||
type server struct {
|
type server struct {
|
||||||
|
|
Loading…
Reference in New Issue