ReflectionService.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /*
  2. * Copyright 2023, gRPC Authors All rights reserved.
  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. */
  16. import DequeModule
  17. import Foundation
  18. import GRPC
  19. import SwiftProtobuf
  20. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  21. public final class ReflectionService: CallHandlerProvider, Sendable {
  22. private let reflectionService: ReflectionServiceProvider
  23. public var serviceName: Substring {
  24. self.reflectionService.serviceName
  25. }
  26. public init(fileDescriptors: [Google_Protobuf_FileDescriptorProto]) throws {
  27. self.reflectionService = try ReflectionServiceProvider(fileDescriptorProtos: fileDescriptors)
  28. }
  29. public func handle(
  30. method name: Substring,
  31. context: GRPC.CallHandlerContext
  32. ) -> GRPC.GRPCServerHandlerProtocol? {
  33. self.reflectionService.handle(method: name, context: context)
  34. }
  35. }
  36. internal struct ReflectionServiceData: Sendable {
  37. internal struct FileDescriptorProtoData: Sendable {
  38. internal var serializedFileDescriptorProto: Data
  39. internal var dependencyFileNames: [String]
  40. }
  41. private struct ExtensionDescriptor: Sendable, Hashable {
  42. internal let extendeeTypeName: String
  43. internal let fieldNumber: Int32
  44. }
  45. internal var fileDescriptorDataByFilename: [String: FileDescriptorProtoData]
  46. internal var serviceNames: [String]
  47. internal var fileNameBySymbol: [String: String]
  48. private var fileNameByExtensionDescriptor: [ExtensionDescriptor: String]
  49. internal init(fileDescriptors: [Google_Protobuf_FileDescriptorProto]) throws {
  50. self.serviceNames = []
  51. self.fileDescriptorDataByFilename = [:]
  52. self.fileNameBySymbol = [:]
  53. self.fileNameByExtensionDescriptor = [:]
  54. for fileDescriptorProto in fileDescriptors {
  55. let serializedFileDescriptorProto: Data
  56. do {
  57. serializedFileDescriptorProto = try fileDescriptorProto.serializedData()
  58. } catch {
  59. throw GRPCStatus(
  60. code: .invalidArgument,
  61. message:
  62. "The \(fileDescriptorProto.name) could not be serialized."
  63. )
  64. }
  65. let protoData = FileDescriptorProtoData(
  66. serializedFileDescriptorProto: serializedFileDescriptorProto,
  67. dependencyFileNames: fileDescriptorProto.dependency
  68. )
  69. self.fileDescriptorDataByFilename[fileDescriptorProto.name] = protoData
  70. self.serviceNames.append(contentsOf: fileDescriptorProto.service.map { $0.name })
  71. // Populating the <symbol, file name> dictionary.
  72. for qualifiedSybolName in fileDescriptorProto.qualifiedSymbolNames {
  73. let oldValue = self.fileNameBySymbol.updateValue(
  74. fileDescriptorProto.name,
  75. forKey: qualifiedSybolName
  76. )
  77. if let oldValue = oldValue {
  78. throw GRPCStatus(
  79. code: .alreadyExists,
  80. message:
  81. "The \(qualifiedSybolName) symbol from \(fileDescriptorProto.name) already exists in \(oldValue)."
  82. )
  83. }
  84. }
  85. // Populating the <extension descriptor, file name> dictionary.
  86. for `extension` in fileDescriptorProto.extension {
  87. let extensionDescriptor = ExtensionDescriptor(
  88. extendeeTypeName: `extension`.extendee,
  89. fieldNumber: `extension`.number
  90. )
  91. let oldFileName = self.fileNameByExtensionDescriptor.updateValue(
  92. fileDescriptorProto.name,
  93. forKey: extensionDescriptor
  94. )
  95. if let oldFileName = oldFileName {
  96. throw GRPCStatus(
  97. code: .alreadyExists,
  98. message:
  99. """
  100. The extension of the \(extensionDescriptor.extendeeTypeName) type with the field number equal to \
  101. \(extensionDescriptor.fieldNumber) from \(fileDescriptorProto.name) already exists in \(oldFileName).
  102. """
  103. )
  104. }
  105. }
  106. }
  107. }
  108. internal func serialisedFileDescriptorProtosForDependenciesOfFile(
  109. named fileName: String
  110. ) throws -> [Data] {
  111. var toVisit = Deque<String>()
  112. var visited = Set<String>()
  113. var serializedFileDescriptorProtos: [Data] = []
  114. toVisit.append(fileName)
  115. while let currentFileName = toVisit.popFirst() {
  116. if let protoData = self.fileDescriptorDataByFilename[currentFileName] {
  117. toVisit.append(
  118. contentsOf: protoData.dependencyFileNames
  119. .filter { name in
  120. return !visited.contains(name)
  121. }
  122. )
  123. let serializedFileDescriptorProto = protoData.serializedFileDescriptorProto
  124. serializedFileDescriptorProtos.append(serializedFileDescriptorProto)
  125. } else {
  126. throw GRPCStatus(
  127. code: .notFound,
  128. message: "The provided file or a dependency of the provided file could not be found."
  129. )
  130. }
  131. visited.insert(currentFileName)
  132. }
  133. return serializedFileDescriptorProtos
  134. }
  135. internal func nameOfFileContainingSymbol(named symbolName: String) -> String? {
  136. return self.fileNameBySymbol[symbolName]
  137. }
  138. internal func nameOfFileContainingExtension(
  139. named extendeeName: String,
  140. fieldNumber number: Int32
  141. ) -> String? {
  142. let key = ExtensionDescriptor(extendeeTypeName: extendeeName, fieldNumber: number)
  143. return self.fileNameByExtensionDescriptor[key]
  144. }
  145. }
  146. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  147. internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsyncProvider {
  148. private let protoRegistry: ReflectionServiceData
  149. internal init(fileDescriptorProtos: [Google_Protobuf_FileDescriptorProto]) throws {
  150. self.protoRegistry = try ReflectionServiceData(
  151. fileDescriptors: fileDescriptorProtos
  152. )
  153. }
  154. internal func findFileByFileName(
  155. _ fileName: String,
  156. request: Reflection_ServerReflectionRequest
  157. ) throws -> Reflection_ServerReflectionResponse {
  158. return Reflection_ServerReflectionResponse(
  159. request: request,
  160. fileDescriptorResponse: try .with {
  161. $0.fileDescriptorProto = try self.protoRegistry
  162. .serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
  163. }
  164. )
  165. }
  166. internal func getServicesNames(
  167. request: Reflection_ServerReflectionRequest
  168. ) throws -> Reflection_ServerReflectionResponse {
  169. var listServicesResponse = Reflection_ListServiceResponse()
  170. listServicesResponse.service = self.protoRegistry.serviceNames.map { serviceName in
  171. Reflection_ServiceResponse.with {
  172. $0.name = serviceName
  173. }
  174. }
  175. return Reflection_ServerReflectionResponse(
  176. request: request,
  177. listServicesResponse: listServicesResponse
  178. )
  179. }
  180. internal func findFileBySymbol(
  181. _ symbolName: String,
  182. request: Reflection_ServerReflectionRequest
  183. ) throws -> Reflection_ServerReflectionResponse {
  184. guard let fileName = self.protoRegistry.nameOfFileContainingSymbol(named: symbolName) else {
  185. throw GRPCStatus(
  186. code: .notFound,
  187. message: "The provided symbol could not be found."
  188. )
  189. }
  190. return try self.findFileByFileName(fileName, request: request)
  191. }
  192. internal func findFileByExtension(
  193. extensionRequest: Reflection_ExtensionRequest,
  194. request: Reflection_ServerReflectionRequest
  195. ) throws -> Reflection_ServerReflectionResponse {
  196. guard
  197. let fileName = self.protoRegistry.nameOfFileContainingExtension(
  198. named: extensionRequest.containingType,
  199. fieldNumber: extensionRequest.extensionNumber
  200. )
  201. else {
  202. throw GRPCStatus(
  203. code: .notFound,
  204. message: "The provided extension could not be found."
  205. )
  206. }
  207. return try self.findFileByFileName(fileName, request: request)
  208. }
  209. internal func serverReflectionInfo(
  210. requestStream: GRPCAsyncRequestStream<Reflection_ServerReflectionRequest>,
  211. responseStream: GRPCAsyncResponseStreamWriter<Reflection_ServerReflectionResponse>,
  212. context: GRPCAsyncServerCallContext
  213. ) async throws {
  214. for try await request in requestStream {
  215. switch request.messageRequest {
  216. case let .fileByFilename(fileName):
  217. let response = try self.findFileByFileName(
  218. fileName,
  219. request: request
  220. )
  221. try await responseStream.send(response)
  222. case .listServices:
  223. let response = try self.getServicesNames(request: request)
  224. try await responseStream.send(response)
  225. case let .fileContainingSymbol(symbolName):
  226. let response = try self.findFileBySymbol(
  227. symbolName,
  228. request: request
  229. )
  230. try await responseStream.send(response)
  231. case let .fileContainingExtension(extensionRequest):
  232. let response = try self.findFileByExtension(
  233. extensionRequest: extensionRequest,
  234. request: request
  235. )
  236. try await responseStream.send(response)
  237. default:
  238. throw GRPCStatus(code: .unimplemented)
  239. }
  240. }
  241. }
  242. }
  243. extension Reflection_ServerReflectionResponse {
  244. init(
  245. request: Reflection_ServerReflectionRequest,
  246. fileDescriptorResponse: Reflection_FileDescriptorResponse
  247. ) {
  248. self = .with {
  249. $0.validHost = request.host
  250. $0.originalRequest = request
  251. $0.fileDescriptorResponse = fileDescriptorResponse
  252. }
  253. }
  254. init(
  255. request: Reflection_ServerReflectionRequest,
  256. listServicesResponse: Reflection_ListServiceResponse
  257. ) {
  258. self = .with {
  259. $0.validHost = request.host
  260. $0.originalRequest = request
  261. $0.listServicesResponse = listServicesResponse
  262. }
  263. }
  264. }
  265. extension Google_Protobuf_FileDescriptorProto {
  266. var qualifiedServiceAndMethodNames: [String] {
  267. var names: [String] = []
  268. for service in self.service {
  269. names.append(self.package + "." + service.name)
  270. names.append(
  271. contentsOf: service.method
  272. .map { self.package + "." + service.name + "." + $0.name }
  273. )
  274. }
  275. return names
  276. }
  277. var qualifiedMessageTypes: [String] {
  278. return self.messageType.map {
  279. self.package + "." + $0.name
  280. }
  281. }
  282. var qualifiedEnumTypes: [String] {
  283. return self.enumType.map {
  284. self.package + "." + $0.name
  285. }
  286. }
  287. var qualifiedSymbolNames: [String] {
  288. var names = self.qualifiedServiceAndMethodNames
  289. names.append(contentsOf: self.qualifiedMessageTypes)
  290. names.append(contentsOf: self.qualifiedEnumTypes)
  291. return names
  292. }
  293. }