github.com/Big-big-orange/protoreflect@v0.0.0-20240408141420-285cedfdf6a4/grpcreflect/client.go (about) 1 package grpcreflect 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "reflect" 9 "runtime" 10 "sync" 11 "time" 12 13 "github.com/golang/protobuf/proto" 14 "google.golang.org/grpc" 15 "google.golang.org/grpc/codes" 16 refv1 "google.golang.org/grpc/reflection/grpc_reflection_v1" 17 refv1alpha "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" 18 "google.golang.org/grpc/status" 19 "google.golang.org/protobuf/types/descriptorpb" 20 21 "github.com/Big-big-orange/protoreflect/desc" 22 "github.com/Big-big-orange/protoreflect/internal" 23 ) 24 25 // If we try the v1 reflection API and get back "not implemented", we'll wait 26 // this long before trying v1 again. This allows a long-lived client to 27 // dynamically switch from v1alpha to v1 if the underlying server is updated 28 // to support it. But it also prevents every stream request from always trying 29 // v1 first: if we try it and see it fail, we shouldn't continually retry it 30 // if we expect it will fail again. 31 const durationBetweenV1Attempts = time.Hour 32 33 // elementNotFoundError is the error returned by reflective operations where the 34 // server does not recognize a given file name, symbol name, or extension. 35 type elementNotFoundError struct { 36 name string 37 kind elementKind 38 symType symbolType // only used when kind == elementKindSymbol 39 tag int32 // only used when kind == elementKindExtension 40 41 // only errors with a kind of elementKindFile will have a cause, which means 42 // the named file count not be resolved because of a dependency that could 43 // not be found where cause describes the missing dependency 44 cause *elementNotFoundError 45 } 46 47 type elementKind int 48 49 const ( 50 elementKindSymbol elementKind = iota 51 elementKindFile 52 elementKindExtension 53 ) 54 55 type symbolType string 56 57 const ( 58 symbolTypeService = "Service" 59 symbolTypeMessage = "Message" 60 symbolTypeEnum = "Enum" 61 symbolTypeUnknown = "Symbol" 62 ) 63 64 func symbolNotFound(symbol string, symType symbolType, cause *elementNotFoundError) error { 65 return &elementNotFoundError{name: symbol, symType: symType, kind: elementKindSymbol, cause: cause} 66 } 67 68 func extensionNotFound(extendee string, tag int32, cause *elementNotFoundError) error { 69 return &elementNotFoundError{name: extendee, tag: tag, kind: elementKindExtension, cause: cause} 70 } 71 72 func fileNotFound(file string, cause *elementNotFoundError) error { 73 return &elementNotFoundError{name: file, kind: elementKindFile, cause: cause} 74 } 75 76 func (e *elementNotFoundError) Error() string { 77 first := true 78 var b bytes.Buffer 79 for ; e != nil; e = e.cause { 80 if first { 81 first = false 82 } else { 83 fmt.Fprint(&b, "\ncaused by: ") 84 } 85 switch e.kind { 86 case elementKindSymbol: 87 fmt.Fprintf(&b, "%s not found: %s", e.symType, e.name) 88 case elementKindExtension: 89 fmt.Fprintf(&b, "Extension not found: tag %d for %s", e.tag, e.name) 90 default: 91 fmt.Fprintf(&b, "File not found: %s", e.name) 92 } 93 } 94 return b.String() 95 } 96 97 // IsElementNotFoundError determines if the given error indicates that a file 98 // name, symbol name, or extension field was could not be found by the server. 99 func IsElementNotFoundError(err error) bool { 100 _, ok := err.(*elementNotFoundError) 101 return ok 102 } 103 104 // ProtocolError is an error returned when the server sends a response of the 105 // wrong type. 106 type ProtocolError struct { 107 missingType reflect.Type 108 } 109 110 func (p ProtocolError) Error() string { 111 return fmt.Sprintf("Protocol error: response was missing %v", p.missingType) 112 } 113 114 type extDesc struct { 115 extendedMessageName string 116 extensionNumber int32 117 } 118 119 // Client is a client connection to a server for performing reflection calls 120 // and resolving remote symbols. 121 type Client struct { 122 ctx context.Context 123 now func() time.Time 124 stubV1 refv1.ServerReflectionClient 125 stubV1Alpha refv1alpha.ServerReflectionClient 126 127 connMu sync.Mutex 128 cancel context.CancelFunc 129 stream refv1alpha.ServerReflection_ServerReflectionInfoClient 130 useV1Alpha bool 131 lastTriedV1 time.Time 132 133 cacheMu sync.RWMutex 134 protosByName map[string]*descriptorpb.FileDescriptorProto 135 filesByName map[string]*desc.FileDescriptor 136 filesBySymbol map[string]*desc.FileDescriptor 137 filesByExtension map[extDesc]*desc.FileDescriptor 138 } 139 140 // NewClient creates a new Client with the given root context and using the 141 // given RPC stub for talking to the server. 142 // 143 // Deprecated: Use NewClientV1Alpha if you are intentionally pinning the 144 // v1alpha version of the reflection service. Otherwise, use NewClientAuto 145 // instead. 146 func NewClient(ctx context.Context, stub refv1alpha.ServerReflectionClient) *Client { 147 return NewClientV1Alpha(ctx, stub) 148 } 149 150 // NewClientV1Alpha creates a new Client using the v1alpha version of reflection 151 // with the given root context and using the given RPC stub for talking to the 152 // server. 153 func NewClientV1Alpha(ctx context.Context, stub refv1alpha.ServerReflectionClient) *Client { 154 return newClient(ctx, nil, stub) 155 } 156 157 func newClient(ctx context.Context, stubv1 refv1.ServerReflectionClient, stubv1alpha refv1alpha.ServerReflectionClient) *Client { 158 cr := &Client{ 159 ctx: ctx, 160 now: time.Now, 161 stubV1: stubv1, 162 stubV1Alpha: stubv1alpha, 163 protosByName: map[string]*descriptorpb.FileDescriptorProto{}, 164 filesByName: map[string]*desc.FileDescriptor{}, 165 filesBySymbol: map[string]*desc.FileDescriptor{}, 166 filesByExtension: map[extDesc]*desc.FileDescriptor{}, 167 } 168 // don't leak a grpc stream 169 runtime.SetFinalizer(cr, (*Client).Reset) 170 return cr 171 } 172 173 // NewClientAuto creates a new Client that will use either v1 or v1alpha version 174 // of reflection (based on what the server supports) with the given root context 175 // and using the given client connection. 176 // 177 // It will first the v1 version of the reflection service. If it gets back an 178 // "Unimplemented" error, it will fall back to using the v1alpha version. It 179 // will remember which version the server supports for any subsequent operations 180 // that need to re-invoke the streaming RPC. But, if it's a very long-lived 181 // client, it will periodically retry the v1 version (in case the server is 182 // updated to support it also). The period for these retries is every hour. 183 func NewClientAuto(ctx context.Context, cc grpc.ClientConnInterface) *Client { 184 stubv1 := refv1.NewServerReflectionClient(cc) 185 stubv1alpha := refv1alpha.NewServerReflectionClient(cc) 186 return newClient(ctx, stubv1, stubv1alpha) 187 } 188 189 // TODO: We should also have a NewClientV1. However that should not refer to internal 190 // generated code. So it will have to wait until the grpc-go team fixes this issue: 191 // https://github.com/grpc/grpc-go/issues/5684 192 193 // FileByFilename asks the server for a file descriptor for the proto file with 194 // the given name. 195 func (cr *Client) FileByFilename(filename string) (*desc.FileDescriptor, error) { 196 // hit the cache first 197 cr.cacheMu.RLock() 198 if fd, ok := cr.filesByName[filename]; ok { 199 cr.cacheMu.RUnlock() 200 return fd, nil 201 } 202 fdp, ok := cr.protosByName[filename] 203 cr.cacheMu.RUnlock() 204 // not there? see if we've downloaded the proto 205 if ok { 206 return cr.descriptorFromProto(fdp) 207 } 208 209 req := &refv1alpha.ServerReflectionRequest{ 210 MessageRequest: &refv1alpha.ServerReflectionRequest_FileByFilename{ 211 FileByFilename: filename, 212 }, 213 } 214 accept := func(fd *desc.FileDescriptor) bool { 215 return fd.GetName() == filename 216 } 217 218 fd, err := cr.getAndCacheFileDescriptors(req, filename, "", accept) 219 if isNotFound(err) { 220 // file not found? see if we can look up via alternate name 221 if alternate, ok := internal.StdFileAliases[filename]; ok { 222 req := &refv1alpha.ServerReflectionRequest{ 223 MessageRequest: &refv1alpha.ServerReflectionRequest_FileByFilename{ 224 FileByFilename: alternate, 225 }, 226 } 227 fd, err = cr.getAndCacheFileDescriptors(req, alternate, filename, accept) 228 if isNotFound(err) { 229 err = fileNotFound(filename, nil) 230 } 231 } else { 232 err = fileNotFound(filename, nil) 233 } 234 } else if e, ok := err.(*elementNotFoundError); ok { 235 err = fileNotFound(filename, e) 236 } 237 return fd, err 238 } 239 240 // FileContainingSymbol asks the server for a file descriptor for the proto file 241 // that declares the given fully-qualified symbol. 242 func (cr *Client) FileContainingSymbol(symbol string) (*desc.FileDescriptor, error) { 243 // hit the cache first 244 cr.cacheMu.RLock() 245 fd, ok := cr.filesBySymbol[symbol] 246 cr.cacheMu.RUnlock() 247 if ok { 248 return fd, nil 249 } 250 251 req := &refv1alpha.ServerReflectionRequest{ 252 MessageRequest: &refv1alpha.ServerReflectionRequest_FileContainingSymbol{ 253 FileContainingSymbol: symbol, 254 }, 255 } 256 accept := func(fd *desc.FileDescriptor) bool { 257 return fd.FindSymbol(symbol) != nil 258 } 259 fd, err := cr.getAndCacheFileDescriptors(req, "", "", accept) 260 if isNotFound(err) { 261 err = symbolNotFound(symbol, symbolTypeUnknown, nil) 262 } else if e, ok := err.(*elementNotFoundError); ok { 263 err = symbolNotFound(symbol, symbolTypeUnknown, e) 264 } 265 return fd, err 266 } 267 268 // FileContainingExtension asks the server for a file descriptor for the proto 269 // file that declares an extension with the given number for the given 270 // fully-qualified message name. 271 func (cr *Client) FileContainingExtension(extendedMessageName string, extensionNumber int32) (*desc.FileDescriptor, error) { 272 // hit the cache first 273 cr.cacheMu.RLock() 274 fd, ok := cr.filesByExtension[extDesc{extendedMessageName, extensionNumber}] 275 cr.cacheMu.RUnlock() 276 if ok { 277 return fd, nil 278 } 279 280 req := &refv1alpha.ServerReflectionRequest{ 281 MessageRequest: &refv1alpha.ServerReflectionRequest_FileContainingExtension{ 282 FileContainingExtension: &refv1alpha.ExtensionRequest{ 283 ContainingType: extendedMessageName, 284 ExtensionNumber: extensionNumber, 285 }, 286 }, 287 } 288 accept := func(fd *desc.FileDescriptor) bool { 289 return fd.FindExtension(extendedMessageName, extensionNumber) != nil 290 } 291 fd, err := cr.getAndCacheFileDescriptors(req, "", "", accept) 292 if isNotFound(err) { 293 err = extensionNotFound(extendedMessageName, extensionNumber, nil) 294 } else if e, ok := err.(*elementNotFoundError); ok { 295 err = extensionNotFound(extendedMessageName, extensionNumber, e) 296 } 297 return fd, err 298 } 299 300 func (cr *Client) getAndCacheFileDescriptors(req *refv1alpha.ServerReflectionRequest, expectedName, alias string, accept func(*desc.FileDescriptor) bool) (*desc.FileDescriptor, error) { 301 resp, err := cr.send(req) 302 if err != nil { 303 return nil, err 304 } 305 306 fdResp := resp.GetFileDescriptorResponse() 307 if fdResp == nil { 308 return nil, &ProtocolError{reflect.TypeOf(fdResp).Elem()} 309 } 310 311 // Response can contain the result file descriptor, but also its transitive 312 // deps. Furthermore, protocol states that subsequent requests do not need 313 // to send transitive deps that have been sent in prior responses. So we 314 // need to cache all file descriptors and then return the first one (which 315 // should be the answer). If we're looking for a file by name, we can be 316 // smarter and make sure to grab one by name instead of just grabbing the 317 // first one. 318 var fds []*descriptorpb.FileDescriptorProto 319 for _, fdBytes := range fdResp.FileDescriptorProto { 320 fd := &descriptorpb.FileDescriptorProto{} 321 if err = proto.Unmarshal(fdBytes, fd); err != nil { 322 return nil, err 323 } 324 325 if expectedName != "" && alias != "" && expectedName != alias && fd.GetName() == expectedName { 326 // we found a file was aliased, so we need to update the proto to reflect that 327 fd.Name = proto.String(alias) 328 } 329 330 cr.cacheMu.Lock() 331 // store in cache of raw descriptor protos, but don't overwrite existing protos 332 if existingFd, ok := cr.protosByName[fd.GetName()]; ok { 333 fd = existingFd 334 } else { 335 cr.protosByName[fd.GetName()] = fd 336 } 337 cr.cacheMu.Unlock() 338 339 fds = append(fds, fd) 340 } 341 342 // find the right result from the files returned 343 for _, fd := range fds { 344 result, err := cr.descriptorFromProto(fd) 345 if err != nil { 346 return nil, err 347 } 348 if accept(result) { 349 return result, nil 350 } 351 } 352 353 return nil, status.Errorf(codes.NotFound, "response does not include expected file") 354 } 355 356 func (cr *Client) descriptorFromProto(fd *descriptorpb.FileDescriptorProto) (*desc.FileDescriptor, error) { 357 deps := make([]*desc.FileDescriptor, len(fd.GetDependency())) 358 for i, depName := range fd.GetDependency() { 359 if dep, err := cr.FileByFilename(depName); err != nil { 360 return nil, err 361 } else { 362 deps[i] = dep 363 } 364 } 365 d, err := desc.CreateFileDescriptor(fd, deps...) 366 if err != nil { 367 return nil, err 368 } 369 d = cr.cacheFile(d) 370 return d, nil 371 } 372 373 func (cr *Client) cacheFile(fd *desc.FileDescriptor) *desc.FileDescriptor { 374 cr.cacheMu.Lock() 375 defer cr.cacheMu.Unlock() 376 377 // cache file descriptor by name, but don't overwrite existing entry 378 // (existing entry could come from concurrent caller) 379 if existingFd, ok := cr.filesByName[fd.GetName()]; ok { 380 return existingFd 381 } 382 cr.filesByName[fd.GetName()] = fd 383 384 // also cache by symbols and extensions 385 for _, m := range fd.GetMessageTypes() { 386 cr.cacheMessageLocked(fd, m) 387 } 388 for _, e := range fd.GetEnumTypes() { 389 cr.filesBySymbol[e.GetFullyQualifiedName()] = fd 390 for _, v := range e.GetValues() { 391 cr.filesBySymbol[v.GetFullyQualifiedName()] = fd 392 } 393 } 394 for _, e := range fd.GetExtensions() { 395 cr.filesBySymbol[e.GetFullyQualifiedName()] = fd 396 cr.filesByExtension[extDesc{e.GetOwner().GetFullyQualifiedName(), e.GetNumber()}] = fd 397 } 398 for _, s := range fd.GetServices() { 399 cr.filesBySymbol[s.GetFullyQualifiedName()] = fd 400 for _, m := range s.GetMethods() { 401 cr.filesBySymbol[m.GetFullyQualifiedName()] = fd 402 } 403 } 404 405 return fd 406 } 407 408 func (cr *Client) cacheMessageLocked(fd *desc.FileDescriptor, md *desc.MessageDescriptor) { 409 cr.filesBySymbol[md.GetFullyQualifiedName()] = fd 410 for _, f := range md.GetFields() { 411 cr.filesBySymbol[f.GetFullyQualifiedName()] = fd 412 } 413 for _, o := range md.GetOneOfs() { 414 cr.filesBySymbol[o.GetFullyQualifiedName()] = fd 415 } 416 for _, e := range md.GetNestedEnumTypes() { 417 cr.filesBySymbol[e.GetFullyQualifiedName()] = fd 418 for _, v := range e.GetValues() { 419 cr.filesBySymbol[v.GetFullyQualifiedName()] = fd 420 } 421 } 422 for _, e := range md.GetNestedExtensions() { 423 cr.filesBySymbol[e.GetFullyQualifiedName()] = fd 424 cr.filesByExtension[extDesc{e.GetOwner().GetFullyQualifiedName(), e.GetNumber()}] = fd 425 } 426 for _, m := range md.GetNestedMessageTypes() { 427 cr.cacheMessageLocked(fd, m) // recurse 428 } 429 } 430 431 // AllExtensionNumbersForType asks the server for all known extension numbers 432 // for the given fully-qualified message name. 433 func (cr *Client) AllExtensionNumbersForType(extendedMessageName string) ([]int32, error) { 434 req := &refv1alpha.ServerReflectionRequest{ 435 MessageRequest: &refv1alpha.ServerReflectionRequest_AllExtensionNumbersOfType{ 436 AllExtensionNumbersOfType: extendedMessageName, 437 }, 438 } 439 resp, err := cr.send(req) 440 if err != nil { 441 if isNotFound(err) { 442 return nil, symbolNotFound(extendedMessageName, symbolTypeMessage, nil) 443 } 444 return nil, err 445 } 446 447 extResp := resp.GetAllExtensionNumbersResponse() 448 if extResp == nil { 449 return nil, &ProtocolError{reflect.TypeOf(extResp).Elem()} 450 } 451 return extResp.ExtensionNumber, nil 452 } 453 454 // ListServices asks the server for the fully-qualified names of all exposed 455 // services. 456 func (cr *Client) ListServices() ([]string, error) { 457 req := &refv1alpha.ServerReflectionRequest{ 458 MessageRequest: &refv1alpha.ServerReflectionRequest_ListServices{ 459 // proto doesn't indicate any purpose for this value and server impl 460 // doesn't actually use it... 461 ListServices: "*", 462 }, 463 } 464 resp, err := cr.send(req) 465 if err != nil { 466 return nil, err 467 } 468 469 listResp := resp.GetListServicesResponse() 470 if listResp == nil { 471 return nil, &ProtocolError{reflect.TypeOf(listResp).Elem()} 472 } 473 serviceNames := make([]string, len(listResp.Service)) 474 for i, s := range listResp.Service { 475 serviceNames[i] = s.Name 476 } 477 return serviceNames, nil 478 } 479 480 func (cr *Client) send(req *refv1alpha.ServerReflectionRequest) (*refv1alpha.ServerReflectionResponse, error) { 481 // we allow one immediate retry, in case we have a stale stream 482 // (e.g. closed by server) 483 resp, err := cr.doSend(req) 484 if err != nil { 485 return nil, err 486 } 487 488 // convert error response messages into errors 489 errResp := resp.GetErrorResponse() 490 if errResp != nil { 491 return nil, status.Errorf(codes.Code(errResp.ErrorCode), "%s", errResp.ErrorMessage) 492 } 493 494 return resp, nil 495 } 496 497 func isNotFound(err error) bool { 498 if err == nil { 499 return false 500 } 501 s, ok := status.FromError(err) 502 return ok && s.Code() == codes.NotFound 503 } 504 505 func (cr *Client) doSend(req *refv1alpha.ServerReflectionRequest) (*refv1alpha.ServerReflectionResponse, error) { 506 // TODO: Streams are thread-safe, so we shouldn't need to lock. But without locking, we'll need more machinery 507 // (goroutines and channels) to ensure that responses are correctly correlated with their requests and thus 508 // delivered in correct oder. 509 cr.connMu.Lock() 510 defer cr.connMu.Unlock() 511 return cr.doSendLocked(0, nil, req) 512 } 513 514 func (cr *Client) doSendLocked(attemptCount int, prevErr error, req *refv1alpha.ServerReflectionRequest) (*refv1alpha.ServerReflectionResponse, error) { 515 if attemptCount >= 3 && prevErr != nil { 516 return nil, prevErr 517 } 518 if (status.Code(prevErr) == codes.Unimplemented || 519 status.Code(prevErr) == codes.Unavailable) && 520 cr.useV1() { 521 // If v1 is unimplemented, fallback to v1alpha. 522 // We also fallback on unavailable because some servers have been 523 // observed to close the connection/cancel the stream, w/out sending 524 // back status or headers, when the service name is not known. When 525 // this happens, the RPC status code is unavailable. 526 // See https://github.com/fullstorydev/grpcurl/issues/434 527 cr.useV1Alpha = true 528 cr.lastTriedV1 = cr.now() 529 } 530 attemptCount++ 531 532 if err := cr.initStreamLocked(); err != nil { 533 return nil, err 534 } 535 536 if err := cr.stream.Send(req); err != nil { 537 if err == io.EOF { 538 // if send returns EOF, must call Recv to get real underlying error 539 _, err = cr.stream.Recv() 540 } 541 cr.resetLocked() 542 return cr.doSendLocked(attemptCount, err, req) 543 } 544 545 resp, err := cr.stream.Recv() 546 if err != nil { 547 cr.resetLocked() 548 return cr.doSendLocked(attemptCount, err, req) 549 } 550 return resp, nil 551 } 552 553 func (cr *Client) initStreamLocked() error { 554 if cr.stream != nil { 555 return nil 556 } 557 var newCtx context.Context 558 newCtx, cr.cancel = context.WithCancel(cr.ctx) 559 if cr.useV1Alpha == true && cr.now().Sub(cr.lastTriedV1) > durationBetweenV1Attempts { 560 // we're due for periodic retry of v1 561 cr.useV1Alpha = false 562 } 563 if cr.useV1() { 564 // try the v1 API 565 streamv1, err := cr.stubV1.ServerReflectionInfo(newCtx) 566 if err == nil { 567 cr.stream = adaptStreamFromV1{streamv1} 568 return nil 569 } 570 if status.Code(err) != codes.Unimplemented { 571 return err 572 } 573 // oh well, fall through below to try v1alpha and update state 574 // so we skip straight to v1alpha next time 575 cr.useV1Alpha = true 576 cr.lastTriedV1 = cr.now() 577 } 578 var err error 579 cr.stream, err = cr.stubV1Alpha.ServerReflectionInfo(newCtx) 580 return err 581 } 582 583 func (cr *Client) useV1() bool { 584 return !cr.useV1Alpha && cr.stubV1 != nil 585 } 586 587 // Reset ensures that any active stream with the server is closed, releasing any 588 // resources. 589 func (cr *Client) Reset() { 590 cr.connMu.Lock() 591 defer cr.connMu.Unlock() 592 cr.resetLocked() 593 } 594 595 func (cr *Client) resetLocked() { 596 if cr.stream != nil { 597 cr.stream.CloseSend() 598 for { 599 // drain the stream, this covers io.EOF too 600 if _, err := cr.stream.Recv(); err != nil { 601 break 602 } 603 } 604 cr.stream = nil 605 } 606 if cr.cancel != nil { 607 cr.cancel() 608 cr.cancel = nil 609 } 610 } 611 612 // ResolveService asks the server to resolve the given fully-qualified service 613 // name into a service descriptor. 614 func (cr *Client) ResolveService(serviceName string) (*desc.ServiceDescriptor, error) { 615 file, err := cr.FileContainingSymbol(serviceName) 616 if err != nil { 617 return nil, setSymbolType(err, serviceName, symbolTypeService) 618 } 619 d := file.FindSymbol(serviceName) 620 if d == nil { 621 return nil, symbolNotFound(serviceName, symbolTypeService, nil) 622 } 623 if s, ok := d.(*desc.ServiceDescriptor); ok { 624 return s, nil 625 } else { 626 return nil, symbolNotFound(serviceName, symbolTypeService, nil) 627 } 628 } 629 630 // ResolveMessage asks the server to resolve the given fully-qualified message 631 // name into a message descriptor. 632 func (cr *Client) ResolveMessage(messageName string) (*desc.MessageDescriptor, error) { 633 file, err := cr.FileContainingSymbol(messageName) 634 if err != nil { 635 return nil, setSymbolType(err, messageName, symbolTypeMessage) 636 } 637 d := file.FindSymbol(messageName) 638 if d == nil { 639 return nil, symbolNotFound(messageName, symbolTypeMessage, nil) 640 } 641 if s, ok := d.(*desc.MessageDescriptor); ok { 642 return s, nil 643 } else { 644 return nil, symbolNotFound(messageName, symbolTypeMessage, nil) 645 } 646 } 647 648 // ResolveEnum asks the server to resolve the given fully-qualified enum name 649 // into an enum descriptor. 650 func (cr *Client) ResolveEnum(enumName string) (*desc.EnumDescriptor, error) { 651 file, err := cr.FileContainingSymbol(enumName) 652 if err != nil { 653 return nil, setSymbolType(err, enumName, symbolTypeEnum) 654 } 655 d := file.FindSymbol(enumName) 656 if d == nil { 657 return nil, symbolNotFound(enumName, symbolTypeEnum, nil) 658 } 659 if s, ok := d.(*desc.EnumDescriptor); ok { 660 return s, nil 661 } else { 662 return nil, symbolNotFound(enumName, symbolTypeEnum, nil) 663 } 664 } 665 666 func setSymbolType(err error, name string, symType symbolType) error { 667 if e, ok := err.(*elementNotFoundError); ok { 668 if e.kind == elementKindSymbol && e.name == name && e.symType == symbolTypeUnknown { 669 e.symType = symType 670 } 671 } 672 return err 673 } 674 675 // ResolveEnumValues asks the server to resolve the given fully-qualified enum 676 // name into a map of names to numbers that represents the enum's values. 677 func (cr *Client) ResolveEnumValues(enumName string) (map[string]int32, error) { 678 enumDesc, err := cr.ResolveEnum(enumName) 679 if err != nil { 680 return nil, err 681 } 682 vals := map[string]int32{} 683 for _, valDesc := range enumDesc.GetValues() { 684 vals[valDesc.GetName()] = valDesc.GetNumber() 685 } 686 return vals, nil 687 } 688 689 // ResolveExtension asks the server to resolve the given extension number and 690 // fully-qualified message name into a field descriptor. 691 func (cr *Client) ResolveExtension(extendedType string, extensionNumber int32) (*desc.FieldDescriptor, error) { 692 file, err := cr.FileContainingExtension(extendedType, extensionNumber) 693 if err != nil { 694 return nil, err 695 } 696 d := findExtension(extendedType, extensionNumber, fileDescriptorExtensions{file}) 697 if d == nil { 698 return nil, extensionNotFound(extendedType, extensionNumber, nil) 699 } else { 700 return d, nil 701 } 702 } 703 704 func findExtension(extendedType string, extensionNumber int32, scope extensionScope) *desc.FieldDescriptor { 705 // search extensions in this scope 706 for _, ext := range scope.extensions() { 707 if ext.GetNumber() == extensionNumber && ext.GetOwner().GetFullyQualifiedName() == extendedType { 708 return ext 709 } 710 } 711 712 // if not found, search nested scopes 713 for _, nested := range scope.nestedScopes() { 714 ext := findExtension(extendedType, extensionNumber, nested) 715 if ext != nil { 716 return ext 717 } 718 } 719 720 return nil 721 } 722 723 type extensionScope interface { 724 extensions() []*desc.FieldDescriptor 725 nestedScopes() []extensionScope 726 } 727 728 // fileDescriptorExtensions implements extensionHolder interface on top of 729 // FileDescriptorProto 730 type fileDescriptorExtensions struct { 731 proto *desc.FileDescriptor 732 } 733 734 func (fde fileDescriptorExtensions) extensions() []*desc.FieldDescriptor { 735 return fde.proto.GetExtensions() 736 } 737 738 func (fde fileDescriptorExtensions) nestedScopes() []extensionScope { 739 scopes := make([]extensionScope, len(fde.proto.GetMessageTypes())) 740 for i, m := range fde.proto.GetMessageTypes() { 741 scopes[i] = msgDescriptorExtensions{m} 742 } 743 return scopes 744 } 745 746 // msgDescriptorExtensions implements extensionHolder interface on top of 747 // DescriptorProto 748 type msgDescriptorExtensions struct { 749 proto *desc.MessageDescriptor 750 } 751 752 func (mde msgDescriptorExtensions) extensions() []*desc.FieldDescriptor { 753 return mde.proto.GetNestedExtensions() 754 } 755 756 func (mde msgDescriptorExtensions) nestedScopes() []extensionScope { 757 scopes := make([]extensionScope, len(mde.proto.GetNestedMessageTypes())) 758 for i, m := range mde.proto.GetNestedMessageTypes() { 759 scopes[i] = msgDescriptorExtensions{m} 760 } 761 return scopes 762 } 763 764 type adaptStreamFromV1 struct { 765 refv1.ServerReflection_ServerReflectionInfoClient 766 } 767 768 func (a adaptStreamFromV1) Send(request *refv1alpha.ServerReflectionRequest) error { 769 v1req := toV1Request(request) 770 return a.ServerReflection_ServerReflectionInfoClient.Send(v1req) 771 } 772 773 func (a adaptStreamFromV1) Recv() (*refv1alpha.ServerReflectionResponse, error) { 774 v1resp, err := a.ServerReflection_ServerReflectionInfoClient.Recv() 775 if err != nil { 776 return nil, err 777 } 778 return toV1AlphaResponse(v1resp), nil 779 }