ReflectionService.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  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. // Stores the file names for each extension identified by an ExtensionDescriptor object.
  49. private var fileNameByExtensionDescriptor: [ExtensionDescriptor: String]
  50. // Stores the field numbers for each type that has extensions.
  51. private var fieldNumbersByType: [String: [Int32]]
  52. internal init(fileDescriptors: [Google_Protobuf_FileDescriptorProto]) throws {
  53. self.serviceNames = []
  54. self.fileDescriptorDataByFilename = [:]
  55. self.fileNameBySymbol = [:]
  56. self.fileNameByExtensionDescriptor = [:]
  57. self.fieldNumbersByType = [:]
  58. for fileDescriptorProto in fileDescriptors {
  59. let serializedFileDescriptorProto: Data
  60. do {
  61. serializedFileDescriptorProto = try fileDescriptorProto.serializedData()
  62. } catch {
  63. throw GRPCStatus(
  64. code: .invalidArgument,
  65. message:
  66. "The \(fileDescriptorProto.name) could not be serialized."
  67. )
  68. }
  69. let protoData = FileDescriptorProtoData(
  70. serializedFileDescriptorProto: serializedFileDescriptorProto,
  71. dependencyFileNames: fileDescriptorProto.dependency
  72. )
  73. self.fileDescriptorDataByFilename[fileDescriptorProto.name] = protoData
  74. self.serviceNames.append(contentsOf: fileDescriptorProto.service.map { $0.name })
  75. // Populating the <symbol, file name> dictionary.
  76. for qualifiedSybolName in fileDescriptorProto.qualifiedSymbolNames {
  77. let oldValue = self.fileNameBySymbol.updateValue(
  78. fileDescriptorProto.name,
  79. forKey: qualifiedSybolName
  80. )
  81. if let oldValue = oldValue {
  82. throw GRPCStatus(
  83. code: .alreadyExists,
  84. message:
  85. "The \(qualifiedSybolName) symbol from \(fileDescriptorProto.name) already exists in \(oldValue)."
  86. )
  87. }
  88. }
  89. for typeName in fileDescriptorProto.qualifiedMessageTypes {
  90. self.fieldNumbersByType[typeName] = []
  91. }
  92. // Populating the <extension descriptor, file name> dictionary and the <typeName, [FieldNumber]> one.
  93. for `extension` in fileDescriptorProto.extension {
  94. let typeName = String(`extension`.extendee.drop(while: { $0 == "." }))
  95. let extensionDescriptor = ExtensionDescriptor(
  96. extendeeTypeName: typeName,
  97. fieldNumber: `extension`.number
  98. )
  99. let oldFileName = self.fileNameByExtensionDescriptor.updateValue(
  100. fileDescriptorProto.name,
  101. forKey: extensionDescriptor
  102. )
  103. if let oldFileName = oldFileName {
  104. throw GRPCStatus(
  105. code: .alreadyExists,
  106. message:
  107. """
  108. The extension of the \(extensionDescriptor.extendeeTypeName) type with the field number equal to \
  109. \(extensionDescriptor.fieldNumber) from \(fileDescriptorProto.name) already exists in \(oldFileName).
  110. """
  111. )
  112. }
  113. self.fieldNumbersByType[typeName, default: []].append(`extension`.number)
  114. }
  115. }
  116. }
  117. internal func serialisedFileDescriptorProtosForDependenciesOfFile(
  118. named fileName: String
  119. ) throws -> [Data] {
  120. var toVisit = Deque<String>()
  121. var visited = Set<String>()
  122. var serializedFileDescriptorProtos: [Data] = []
  123. toVisit.append(fileName)
  124. while let currentFileName = toVisit.popFirst() {
  125. if let protoData = self.fileDescriptorDataByFilename[currentFileName] {
  126. toVisit.append(
  127. contentsOf: protoData.dependencyFileNames
  128. .filter { name in
  129. return !visited.contains(name)
  130. }
  131. )
  132. let serializedFileDescriptorProto = protoData.serializedFileDescriptorProto
  133. serializedFileDescriptorProtos.append(serializedFileDescriptorProto)
  134. } else {
  135. throw GRPCStatus(
  136. code: .notFound,
  137. message: "The provided file or a dependency of the provided file could not be found."
  138. )
  139. }
  140. visited.insert(currentFileName)
  141. }
  142. return serializedFileDescriptorProtos
  143. }
  144. internal func nameOfFileContainingSymbol(named symbolName: String) -> String? {
  145. return self.fileNameBySymbol[symbolName]
  146. }
  147. internal func nameOfFileContainingExtension(
  148. extendeeName: String,
  149. fieldNumber number: Int32
  150. ) -> String? {
  151. let key = ExtensionDescriptor(extendeeTypeName: extendeeName, fieldNumber: number)
  152. return self.fileNameByExtensionDescriptor[key]
  153. }
  154. // Returns an empty array if the type has no extensions.
  155. internal func extensionsFieldNumbersOfType(named typeName: String) throws -> [Int32] {
  156. guard let fieldNumbers = self.fieldNumbersByType[typeName] else {
  157. throw GRPCStatus(
  158. code: .invalidArgument,
  159. message: "The provided type is invalid."
  160. )
  161. }
  162. return fieldNumbers
  163. }
  164. }
  165. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  166. internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsyncProvider {
  167. private let protoRegistry: ReflectionServiceData
  168. internal init(fileDescriptorProtos: [Google_Protobuf_FileDescriptorProto]) throws {
  169. self.protoRegistry = try ReflectionServiceData(
  170. fileDescriptors: fileDescriptorProtos
  171. )
  172. }
  173. internal func findFileByFileName(
  174. _ fileName: String,
  175. request: Reflection_ServerReflectionRequest
  176. ) throws -> Reflection_ServerReflectionResponse {
  177. return Reflection_ServerReflectionResponse(
  178. request: request,
  179. fileDescriptorResponse: try .with {
  180. $0.fileDescriptorProto = try self.protoRegistry
  181. .serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
  182. }
  183. )
  184. }
  185. internal func getServicesNames(
  186. request: Reflection_ServerReflectionRequest
  187. ) throws -> Reflection_ServerReflectionResponse {
  188. var listServicesResponse = Reflection_ListServiceResponse()
  189. listServicesResponse.service = self.protoRegistry.serviceNames.map { serviceName in
  190. Reflection_ServiceResponse.with {
  191. $0.name = serviceName
  192. }
  193. }
  194. return Reflection_ServerReflectionResponse(
  195. request: request,
  196. listServicesResponse: listServicesResponse
  197. )
  198. }
  199. internal func findFileBySymbol(
  200. _ symbolName: String,
  201. request: Reflection_ServerReflectionRequest
  202. ) throws -> Reflection_ServerReflectionResponse {
  203. guard let fileName = self.protoRegistry.nameOfFileContainingSymbol(named: symbolName) else {
  204. throw GRPCStatus(
  205. code: .notFound,
  206. message: "The provided symbol could not be found."
  207. )
  208. }
  209. return try self.findFileByFileName(fileName, request: request)
  210. }
  211. internal func findFileByExtension(
  212. extensionRequest: Reflection_ExtensionRequest,
  213. request: Reflection_ServerReflectionRequest
  214. ) throws -> Reflection_ServerReflectionResponse {
  215. guard
  216. let fileName = self.protoRegistry.nameOfFileContainingExtension(
  217. extendeeName: extensionRequest.containingType,
  218. fieldNumber: extensionRequest.extensionNumber
  219. )
  220. else {
  221. throw GRPCStatus(
  222. code: .notFound,
  223. message: "The provided extension could not be found."
  224. )
  225. }
  226. return try self.findFileByFileName(fileName, request: request)
  227. }
  228. internal func findExtensionsFieldNumbersOfType(
  229. named typeName: String,
  230. request: Reflection_ServerReflectionRequest
  231. ) throws -> Reflection_ServerReflectionResponse {
  232. let fieldNumbers = try self.protoRegistry.extensionsFieldNumbersOfType(named: typeName)
  233. return Reflection_ServerReflectionResponse(
  234. request: request,
  235. extensionNumberResponse: .with {
  236. $0.baseTypeName = typeName
  237. $0.extensionNumber = fieldNumbers
  238. }
  239. )
  240. }
  241. internal func serverReflectionInfo(
  242. requestStream: GRPCAsyncRequestStream<Reflection_ServerReflectionRequest>,
  243. responseStream: GRPCAsyncResponseStreamWriter<Reflection_ServerReflectionResponse>,
  244. context: GRPCAsyncServerCallContext
  245. ) async throws {
  246. for try await request in requestStream {
  247. switch request.messageRequest {
  248. case let .fileByFilename(fileName):
  249. let response = try self.findFileByFileName(
  250. fileName,
  251. request: request
  252. )
  253. try await responseStream.send(response)
  254. case .listServices:
  255. let response = try self.getServicesNames(request: request)
  256. try await responseStream.send(response)
  257. case let .fileContainingSymbol(symbolName):
  258. let response = try self.findFileBySymbol(
  259. symbolName,
  260. request: request
  261. )
  262. try await responseStream.send(response)
  263. case let .fileContainingExtension(extensionRequest):
  264. let response = try self.findFileByExtension(
  265. extensionRequest: extensionRequest,
  266. request: request
  267. )
  268. try await responseStream.send(response)
  269. case let .allExtensionNumbersOfType(typeName):
  270. let response = try self.findExtensionsFieldNumbersOfType(
  271. named: typeName,
  272. request: request
  273. )
  274. try await responseStream.send(response)
  275. default:
  276. throw GRPCStatus(code: .unimplemented)
  277. }
  278. }
  279. }
  280. }
  281. extension Reflection_ServerReflectionResponse {
  282. init(
  283. request: Reflection_ServerReflectionRequest,
  284. fileDescriptorResponse: Reflection_FileDescriptorResponse
  285. ) {
  286. self = .with {
  287. $0.validHost = request.host
  288. $0.originalRequest = request
  289. $0.fileDescriptorResponse = fileDescriptorResponse
  290. }
  291. }
  292. init(
  293. request: Reflection_ServerReflectionRequest,
  294. listServicesResponse: Reflection_ListServiceResponse
  295. ) {
  296. self = .with {
  297. $0.validHost = request.host
  298. $0.originalRequest = request
  299. $0.listServicesResponse = listServicesResponse
  300. }
  301. }
  302. init(
  303. request: Reflection_ServerReflectionRequest,
  304. extensionNumberResponse: Reflection_ExtensionNumberResponse
  305. ) {
  306. self = .with {
  307. $0.validHost = request.host
  308. $0.originalRequest = request
  309. $0.allExtensionNumbersResponse = extensionNumberResponse
  310. }
  311. }
  312. }
  313. extension Google_Protobuf_FileDescriptorProto {
  314. var qualifiedServiceAndMethodNames: [String] {
  315. var names: [String] = []
  316. for service in self.service {
  317. names.append(self.package + "." + service.name)
  318. names.append(
  319. contentsOf: service.method
  320. .map { self.package + "." + service.name + "." + $0.name }
  321. )
  322. }
  323. return names
  324. }
  325. var qualifiedMessageTypes: [String] {
  326. return self.messageType.map {
  327. self.package + "." + $0.name
  328. }
  329. }
  330. var qualifiedEnumTypes: [String] {
  331. return self.enumType.map {
  332. self.package + "." + $0.name
  333. }
  334. }
  335. var qualifiedSymbolNames: [String] {
  336. var names = self.qualifiedServiceAndMethodNames
  337. names.append(contentsOf: self.qualifiedMessageTypes)
  338. names.append(contentsOf: self.qualifiedEnumTypes)
  339. return names
  340. }
  341. }