-
Notifications
You must be signed in to change notification settings - Fork 4.2k
gRPC: fix server reflection #8945
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
Changes from all commits
d4db860
5c11486
034b0d8
cb21912
9b2800f
0d82b78
79d481e
5875f01
d1a1ff9
6567491
716a145
03f2c8d
ed0cd48
b7c26e4
a0a7f84
ba4698c
b26b502
def738b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| // Package gogoreflection implements gRPC reflection for gogoproto consumers | ||
| // the normal reflection library does not work as it points to a different | ||
| // singleton registry. The API and codebase is taken from the official gRPC | ||
| // reflection repository. | ||
| package gogoreflection |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| package gogoreflection | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "compress/gzip" | ||
| "fmt" | ||
| "reflect" | ||
|
|
||
| _ "github.com/gogo/protobuf/gogoproto" // required so it does register the gogoproto file descriptor | ||
| gogoproto "github.com/gogo/protobuf/proto" | ||
|
|
||
| // nolint: staticcheck | ||
| "github.com/golang/protobuf/proto" | ||
| dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" | ||
| _ "github.com/regen-network/cosmos-proto" // look above | ||
| ) | ||
|
|
||
| var importsToFix = map[string]string{ | ||
| "gogo.proto": "gogoproto/gogo.proto", | ||
| "cosmos.proto": "cosmos_proto/cosmos.proto", | ||
| } | ||
|
|
||
| // fixRegistration is required because certain files register themselves in a way | ||
| // but are imported by other files in a different way. | ||
fdymylja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // NOTE(fdymylja): This fix should not be needed and should be addressed in some CI. | ||
| // Currently every cosmos-sdk proto file is importing gogo.proto as gogoproto/gogo.proto, | ||
| // but gogo.proto registers itself as gogo.proto, same goes for cosmos.proto. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you know why this happens? Maybe we should fix it in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean - let's create an issue if there is a bug we can easily fix in cosmos-proto
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's not a bug per se, it's a shortcoming when it comes to combo together reflection and proto imports. protofiles register themselve in the registry with a path, but then are imported with another path. this makes reflection fail, because it cannot find certain files. this would mean either forking gogoproto and changing how we feed the file to protoc, or changing the third_party structure in the sdk, edit how every file imports gogoproto. and im not sure if this is a breaking change, descriptor wise it is. probably this should be a blob of work which should be reorganized during the transition to protov2 |
||
| func fixRegistration(registeredAs, importedAs string) error { | ||
| raw := gogoproto.FileDescriptor(registeredAs) | ||
| if len(raw) == 0 { | ||
| return fmt.Errorf("file descriptor not found for %s", registeredAs) | ||
| } | ||
|
|
||
| fd, err := decodeFileDesc(raw) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // fix name | ||
| *fd.Name = importedAs | ||
| fixedRaw, err := compress(fd) | ||
| if err != nil { | ||
| return fmt.Errorf("unable to compress: %w", err) | ||
| } | ||
| gogoproto.RegisterFile(importedAs, fixedRaw) | ||
| return nil | ||
| } | ||
|
|
||
| func init() { | ||
| // we need to fix the gogoproto filedesc to match the import path | ||
| // in theory this shouldn't be required, generally speaking | ||
| // proto files should be imported as their registration path | ||
|
|
||
| for registeredAs, importedAs := range importsToFix { | ||
| err := fixRegistration(registeredAs, importedAs) | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // compress compresses the given file descriptor | ||
| // nolint: interfacer | ||
| func compress(fd *dpb.FileDescriptorProto) ([]byte, error) { | ||
| fdBytes, err := proto.Marshal(fd) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| buf := new(bytes.Buffer) | ||
| cw := gzip.NewWriter(buf) | ||
| _, err = cw.Write(fdBytes) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| err = cw.Close() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return buf.Bytes(), nil | ||
| } | ||
|
|
||
| func getFileDescriptor(filePath string) []byte { | ||
| // since we got well known descriptors which are not registered into gogoproto registry | ||
| // but are instead registered into the proto one, we need to check both | ||
| fd := gogoproto.FileDescriptor(filePath) | ||
| if len(fd) != 0 { | ||
| return fd | ||
| } | ||
| // nolint: staticcheck | ||
fdymylja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return proto.FileDescriptor(filePath) | ||
| } | ||
|
|
||
| func getMessageType(name string) reflect.Type { | ||
| typ := gogoproto.MessageType(name) | ||
| if typ != nil { | ||
| return typ | ||
| } | ||
| // nolint: staticcheck | ||
| return proto.MessageType(name) | ||
| } | ||
|
|
||
| func getExtension(extID int32, m proto.Message) *gogoproto.ExtensionDesc { | ||
| // check first in gogoproto registry | ||
| for id, desc := range gogoproto.RegisteredExtensions(m) { | ||
| if id == extID { | ||
| return desc | ||
| } | ||
| } | ||
| // check into proto registry | ||
| // nolint: staticcheck | ||
| for id, desc := range proto.RegisteredExtensions(m) { | ||
| if id == extID { | ||
| return &gogoproto.ExtensionDesc{ | ||
| ExtendedType: desc.ExtendedType, | ||
| ExtensionType: desc.ExtensionType, | ||
| Field: desc.Field, | ||
| Name: desc.Name, | ||
| Tag: desc.Tag, | ||
| Filename: desc.Filename, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func getExtensionsNumbers(m proto.Message) []int32 { | ||
| gogoProtoExts := gogoproto.RegisteredExtensions(m) | ||
| out := make([]int32, 0, len(gogoProtoExts)) | ||
| for id := range gogoProtoExts { | ||
| out = append(out, id) | ||
| } | ||
| if len(out) != 0 { | ||
| return out | ||
| } | ||
| // nolint: staticcheck | ||
| protoExts := proto.RegisteredExtensions(m) | ||
| out = make([]int32, 0, len(protoExts)) | ||
| for id := range protoExts { | ||
| out = append(out, id) | ||
| } | ||
| return out | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package gogoreflection | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "google.golang.org/protobuf/runtime/protoimpl" | ||
| ) | ||
|
|
||
| func TestRegistrationFix(t *testing.T) { | ||
| res := getFileDescriptor("gogoproto/gogo.proto") | ||
| rawDesc, err := decompress(res) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| fd := protoimpl.DescBuilder{ | ||
| RawDescriptor: rawDesc, | ||
| }.Build() | ||
|
|
||
| if fd.File.Extensions().Len() == 0 { | ||
| t.Fatal("unexpected parsing") | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.