| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 | package swagger_apiimport (	"bytes"	"compress/gzip"	"context"	"errors"	"fmt"	"io"	"sort"	"sync"	"github.com/go-kratos/kratos/v2/api/metadata"	"google.golang.org/grpc"	"google.golang.org/grpc/codes"	"google.golang.org/grpc/status"	"google.golang.org/protobuf/proto"	"google.golang.org/protobuf/reflect/protodesc"	"google.golang.org/protobuf/reflect/protoreflect"	"google.golang.org/protobuf/reflect/protoregistry"	dpb "google.golang.org/protobuf/types/descriptorpb"	"github.com/go-kratos/kratos/v2/log")// Server is api meta servertype Server struct {	metadata.UnimplementedMetadataServer	srv       *grpc.Server	lock      sync.Mutex	services  map[string]*dpb.FileDescriptorSet	methods   map[string][]string	SkipError bool}// NewServer create server instancefunc NewServer(srv *grpc.Server, skipError bool) *Server {	return &Server{		srv:       srv,		SkipError: skipError,		services:  make(map[string]*dpb.FileDescriptorSet),		methods:   make(map[string][]string),	}}func (s *Server) load() error {	if len(s.services) > 0 {		return nil	}	if s.srv != nil {		for name, info := range s.srv.GetServiceInfo() {			fd, err := s.parseMetadata(info.Metadata)			if err != nil {				return fmt.Errorf("invalid service %s metadata err:%v", name, err)			}			protoSet, err := s.allDependency(fd)			if err != nil {				return err			}			s.services[name] = &dpb.FileDescriptorSet{File: protoSet}			for _, method := range info.Methods {				s.methods[name] = append(s.methods[name], method.Name)			}		}		return nil	}	var err error	protoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {		if fd.Services() == nil {			return true		}		for i := 0; i < fd.Services().Len(); i++ {			svc := fd.Services().Get(i)			fdp, e := fileDescriptorProto(fd.Path())			if e != nil {				if s.SkipError {					continue				}				err = e				return false			}			fdps, e := s.allDependency(fdp)			if e != nil {				if errors.Is(e, protoregistry.NotFound) {					// Skip this service if one of its dependencies is not found.					continue				}				err = e				return false			}			s.services[string(svc.FullName())] = &dpb.FileDescriptorSet{File: fdps}			if svc.Methods() == nil {				continue			}			for j := 0; j < svc.Methods().Len(); j++ {				method := svc.Methods().Get(j)				s.methods[string(svc.FullName())] = append(s.methods[string(svc.FullName())], string(method.Name()))			}		}		return true	})	return err}// ListServices return all servicesfunc (s *Server) ListServices(_ context.Context, _ *metadata.ListServicesRequest) (*metadata.ListServicesReply, error) {	s.lock.Lock()	defer s.lock.Unlock()	if err := s.load(); err != nil {		return nil, err	}	reply := &metadata.ListServicesReply{		Services: make([]string, 0, len(s.services)),		Methods:  make([]string, 0, len(s.methods)),	}	for name := range s.services {		reply.Services = append(reply.Services, name)	}	for name, methods := range s.methods {		for _, method := range methods {			reply.Methods = append(reply.Methods, fmt.Sprintf("/%s/%s", name, method))		}	}	sort.Strings(reply.Services)	sort.Strings(reply.Methods)	return reply, nil}// GetServiceDesc return service meta by namefunc (s *Server) GetServiceDesc(_ context.Context, in *metadata.GetServiceDescRequest) (*metadata.GetServiceDescReply, error) {	s.lock.Lock()	defer s.lock.Unlock()	if err := s.load(); err != nil {		return nil, err	}	fds, ok := s.services[in.Name]	if !ok {		return nil, status.Errorf(codes.NotFound, "service %s not found", in.Name)	}	return &metadata.GetServiceDescReply{FileDescSet: fds}, nil}// parseMetadata finds the file descriptor bytes specified meta.// For SupportPackageIsVersion4, m is the name of the proto file, we// call proto.FileDescriptor to get the byte slice.// For SupportPackageIsVersion3, m is a byte slice itself.func (s *Server) parseMetadata(meta interface{}) (*dpb.FileDescriptorProto, error) {	// Check if meta is the file name.	if fileNameForMeta, ok := meta.(string); ok {		return fileDescriptorProto(fileNameForMeta)	}	// Check if meta is the byte slice.	if enc, ok := meta.([]byte); ok {		return s.decodeFileDesc(enc)	}	return nil, fmt.Errorf("proto not sumpport metadata: %v", meta)}// decodeFileDesc does decompression and unmarshalling on the given// file descriptor byte slice.func (s *Server) decodeFileDesc(enc []byte) (*dpb.FileDescriptorProto, error) {	raw, err := s.decompress(enc)	if err != nil {		return nil, fmt.Errorf("failed to decompress enc: %v", err)	}	fd := new(dpb.FileDescriptorProto)	if err := proto.Unmarshal(raw, fd); err != nil {		return nil, fmt.Errorf("bad descriptor: %v", err)	}	return fd, nil}func (s *Server) allDependency(fd *dpb.FileDescriptorProto) ([]*dpb.FileDescriptorProto, error) {	var files []*dpb.FileDescriptorProto	for _, dep := range fd.Dependency {		fdDep, err := fileDescriptorProto(dep)		if err != nil {			if !s.SkipError {				log.Warnf("%s", err)			}			continue		}		temp, err := s.allDependency(fdDep)		if err != nil {			return nil, err		}		files = append(files, temp...)	}	files = append(files, fd)	return files, nil}// decompress does gzip decompression.func (s *Server) decompress(b []byte) ([]byte, error) {	r, err := gzip.NewReader(bytes.NewReader(b))	if err != nil {		return nil, fmt.Errorf("bad gzipped descriptor: %v", err)	}	out, err := io.ReadAll(r)	if err != nil {		return nil, fmt.Errorf("bad gzipped descriptor: %v", err)	}	return out, nil}func fileDescriptorProto(path string) (*dpb.FileDescriptorProto, error) {	fd, err := protoregistry.GlobalFiles.FindFileByPath(path)	if err != nil {		return nil, fmt.Errorf("find proto by path failed, path: %s, err: %s", path, err)	}	fdpb := protodesc.ToFileDescriptorProto(fd)	return fdpb, nil}
 |