1 #region Copyright notice and license 2 // Copyright 2015 gRPC authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 #endregion 16 17 using System; 18 using System.Collections.Generic; 19 using System.Linq; 20 using System.Text; 21 using System.Threading.Tasks; 22 23 using Grpc.Core; 24 using Grpc.Core.Utils; 25 using Grpc.Reflection.V1Alpha; 26 using Google.Protobuf.Reflection; 27 28 namespace Grpc.Reflection 29 { 30 /// <summary> 31 /// Implementation of server reflection service. 32 /// </summary> 33 public class ReflectionServiceImpl : Grpc.Reflection.V1Alpha.ServerReflection.ServerReflectionBase 34 { 35 readonly List<string> services; 36 readonly SymbolRegistry symbolRegistry; 37 38 /// <summary> 39 /// Creates a new instance of <c>ReflectionServiceIml</c>. 40 /// </summary> ReflectionServiceImpl(IEnumerable<string> services, SymbolRegistry symbolRegistry)41 public ReflectionServiceImpl(IEnumerable<string> services, SymbolRegistry symbolRegistry) 42 { 43 this.services = new List<string>(services); 44 this.symbolRegistry = symbolRegistry; 45 } 46 47 /// <summary> 48 /// Creates a new instance of <c>ReflectionServiceIml</c>. 49 /// </summary> ReflectionServiceImpl(IEnumerable<ServiceDescriptor> serviceDescriptors)50 public ReflectionServiceImpl(IEnumerable<ServiceDescriptor> serviceDescriptors) 51 { 52 this.services = new List<string>(serviceDescriptors.Select((serviceDescriptor) => serviceDescriptor.FullName)); 53 this.symbolRegistry = SymbolRegistry.FromFiles(serviceDescriptors.Select((serviceDescriptor) => serviceDescriptor.File)); 54 } 55 56 /// <summary> 57 /// Creates a new instance of <c>ReflectionServiceIml</c>. 58 /// </summary> ReflectionServiceImpl(params ServiceDescriptor[] serviceDescriptors)59 public ReflectionServiceImpl(params ServiceDescriptor[] serviceDescriptors) : this((IEnumerable<ServiceDescriptor>) serviceDescriptors) 60 { 61 } 62 63 /// <summary> 64 /// Processes a stream of server reflection requests. 65 /// </summary> ServerReflectionInfo(IAsyncStreamReader<ServerReflectionRequest> requestStream, IServerStreamWriter<ServerReflectionResponse> responseStream, ServerCallContext context)66 public override async Task ServerReflectionInfo(IAsyncStreamReader<ServerReflectionRequest> requestStream, IServerStreamWriter<ServerReflectionResponse> responseStream, ServerCallContext context) 67 { 68 while (await requestStream.MoveNext()) 69 { 70 var response = ProcessRequest(requestStream.Current); 71 await responseStream.WriteAsync(response); 72 } 73 } 74 ProcessRequest(ServerReflectionRequest request)75 ServerReflectionResponse ProcessRequest(ServerReflectionRequest request) 76 { 77 switch (request.MessageRequestCase) 78 { 79 case ServerReflectionRequest.MessageRequestOneofCase.FileByFilename: 80 return FileByFilename(request.FileByFilename); 81 case ServerReflectionRequest.MessageRequestOneofCase.FileContainingSymbol: 82 return FileContainingSymbol(request.FileContainingSymbol); 83 case ServerReflectionRequest.MessageRequestOneofCase.ListServices: 84 return ListServices(); 85 case ServerReflectionRequest.MessageRequestOneofCase.AllExtensionNumbersOfType: 86 case ServerReflectionRequest.MessageRequestOneofCase.FileContainingExtension: 87 default: 88 return CreateErrorResponse(StatusCode.Unimplemented, "Request type not supported by C# reflection service."); 89 } 90 } 91 FileByFilename(string filename)92 ServerReflectionResponse FileByFilename(string filename) 93 { 94 FileDescriptor file = symbolRegistry.FileByName(filename); 95 if (file == null) 96 { 97 return CreateErrorResponse(StatusCode.NotFound, "File not found."); 98 } 99 100 var transitiveDependencies = new HashSet<FileDescriptor>(); 101 CollectTransitiveDependencies(file, transitiveDependencies); 102 103 return new ServerReflectionResponse 104 { 105 FileDescriptorResponse = new FileDescriptorResponse { FileDescriptorProto = { transitiveDependencies.Select((d) => d.SerializedData) } } 106 }; 107 } 108 FileContainingSymbol(string symbol)109 ServerReflectionResponse FileContainingSymbol(string symbol) 110 { 111 FileDescriptor file = symbolRegistry.FileContainingSymbol(symbol); 112 if (file == null) 113 { 114 return CreateErrorResponse(StatusCode.NotFound, "Symbol not found."); 115 } 116 117 var transitiveDependencies = new HashSet<FileDescriptor>(); 118 CollectTransitiveDependencies(file, transitiveDependencies); 119 120 return new ServerReflectionResponse 121 { 122 FileDescriptorResponse = new FileDescriptorResponse { FileDescriptorProto = { transitiveDependencies.Select((d) => d.SerializedData) } } 123 }; 124 } 125 ListServices()126 ServerReflectionResponse ListServices() 127 { 128 var serviceResponses = new ListServiceResponse(); 129 foreach (string serviceName in services) 130 { 131 serviceResponses.Service.Add(new ServiceResponse { Name = serviceName }); 132 } 133 134 return new ServerReflectionResponse 135 { 136 ListServicesResponse = serviceResponses 137 }; 138 } 139 CreateErrorResponse(StatusCode status, string message)140 ServerReflectionResponse CreateErrorResponse(StatusCode status, string message) 141 { 142 return new ServerReflectionResponse 143 { 144 ErrorResponse = new ErrorResponse { ErrorCode = (int) status, ErrorMessage = message } 145 }; 146 } 147 CollectTransitiveDependencies(FileDescriptor descriptor, HashSet<FileDescriptor> pool)148 void CollectTransitiveDependencies(FileDescriptor descriptor, HashSet<FileDescriptor> pool) 149 { 150 pool.Add(descriptor); 151 foreach (var dependency in descriptor.Dependencies) 152 { 153 if (pool.Add(dependency)) 154 { 155 // descriptors cannot have circular dependencies 156 CollectTransitiveDependencies(dependency, pool); 157 } 158 } 159 } 160 } 161 } 162