#region Copyright notice and license // Copyright 2015 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #endregion using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Grpc.Core; using Grpc.Core.Utils; using Grpc.Reflection.V1Alpha; using Google.Protobuf.Reflection; namespace Grpc.Reflection { /// /// Implementation of server reflection service. /// public class ReflectionServiceImpl : Grpc.Reflection.V1Alpha.ServerReflection.ServerReflectionBase { readonly List services; readonly SymbolRegistry symbolRegistry; /// /// Creates a new instance of ReflectionServiceIml. /// public ReflectionServiceImpl(IEnumerable services, SymbolRegistry symbolRegistry) { this.services = new List(services); this.symbolRegistry = symbolRegistry; } /// /// Creates a new instance of ReflectionServiceIml. /// public ReflectionServiceImpl(IEnumerable serviceDescriptors) { this.services = new List(serviceDescriptors.Select((serviceDescriptor) => serviceDescriptor.FullName)); this.symbolRegistry = SymbolRegistry.FromFiles(serviceDescriptors.Select((serviceDescriptor) => serviceDescriptor.File)); } /// /// Creates a new instance of ReflectionServiceIml. /// public ReflectionServiceImpl(params ServiceDescriptor[] serviceDescriptors) : this((IEnumerable) serviceDescriptors) { } /// /// Processes a stream of server reflection requests. /// public override async Task ServerReflectionInfo(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) { while (await requestStream.MoveNext()) { var response = ProcessRequest(requestStream.Current); await responseStream.WriteAsync(response); } } ServerReflectionResponse ProcessRequest(ServerReflectionRequest request) { switch (request.MessageRequestCase) { case ServerReflectionRequest.MessageRequestOneofCase.FileByFilename: return FileByFilename(request.FileByFilename); case ServerReflectionRequest.MessageRequestOneofCase.FileContainingSymbol: return FileContainingSymbol(request.FileContainingSymbol); case ServerReflectionRequest.MessageRequestOneofCase.ListServices: return ListServices(); case ServerReflectionRequest.MessageRequestOneofCase.AllExtensionNumbersOfType: case ServerReflectionRequest.MessageRequestOneofCase.FileContainingExtension: default: return CreateErrorResponse(StatusCode.Unimplemented, "Request type not supported by C# reflection service."); } } ServerReflectionResponse FileByFilename(string filename) { FileDescriptor file = symbolRegistry.FileByName(filename); if (file == null) { return CreateErrorResponse(StatusCode.NotFound, "File not found."); } var transitiveDependencies = new HashSet(); CollectTransitiveDependencies(file, transitiveDependencies); return new ServerReflectionResponse { FileDescriptorResponse = new FileDescriptorResponse { FileDescriptorProto = { transitiveDependencies.Select((d) => d.SerializedData) } } }; } ServerReflectionResponse FileContainingSymbol(string symbol) { FileDescriptor file = symbolRegistry.FileContainingSymbol(symbol); if (file == null) { return CreateErrorResponse(StatusCode.NotFound, "Symbol not found."); } var transitiveDependencies = new HashSet(); CollectTransitiveDependencies(file, transitiveDependencies); return new ServerReflectionResponse { FileDescriptorResponse = new FileDescriptorResponse { FileDescriptorProto = { transitiveDependencies.Select((d) => d.SerializedData) } } }; } ServerReflectionResponse ListServices() { var serviceResponses = new ListServiceResponse(); foreach (string serviceName in services) { serviceResponses.Service.Add(new ServiceResponse { Name = serviceName }); } return new ServerReflectionResponse { ListServicesResponse = serviceResponses }; } ServerReflectionResponse CreateErrorResponse(StatusCode status, string message) { return new ServerReflectionResponse { ErrorResponse = new ErrorResponse { ErrorCode = (int) status, ErrorMessage = message } }; } void CollectTransitiveDependencies(FileDescriptor descriptor, HashSet pool) { pool.Add(descriptor); foreach (var dependency in descriptor.Dependencies) { if (pool.Add(dependency)) { // descriptors cannot have circular dependencies CollectTransitiveDependencies(dependency, pool); } } } } }