blob: 1eaf10bfb84e05d2189e30e0ad17824ef220db2d [file] [log] [blame]
Jan Tattermusch02017762016-11-23 15:39:56 +01001#region Copyright notice and license
Jan Tattermusch7897ae92017-06-07 22:57:36 +02002// Copyright 2015 gRPC authors.
Jan Tattermusch02017762016-11-23 15:39:56 +01003//
Jan Tattermusch7897ae92017-06-07 22:57:36 +02004// 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
Jan Tattermusch02017762016-11-23 15:39:56 +01007//
Jan Tattermusch7897ae92017-06-07 22:57:36 +02008// http://www.apache.org/licenses/LICENSE-2.0
Jan Tattermusch02017762016-11-23 15:39:56 +01009//
Jan Tattermusch7897ae92017-06-07 22:57:36 +020010// 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.
Jan Tattermusch02017762016-11-23 15:39:56 +010015#endregion
16
17using System;
18using System.Collections.Generic;
19using System.Linq;
20using System.Text;
21using System.Threading.Tasks;
22
23using Grpc.Core;
24using Grpc.Core.Utils;
25using Grpc.Reflection.V1Alpha;
26using Google.Protobuf.Reflection;
27
28namespace 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>
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>
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>
59 public ReflectionServiceImpl(params ServiceDescriptor[] serviceDescriptors) : this((IEnumerable<ServiceDescriptor>) serviceDescriptors)
60 {
61 }
62
Jan Tattermuschaa135ce2017-08-08 17:24:39 +020063 /// <summary>
64 /// Processes a stream of server reflection requests.
65 /// </summary>
Jan Tattermusch02017762016-11-23 15:39:56 +010066 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
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
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
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
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
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
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}