ReflectionService.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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. ) -> Result<[Data], GRPCStatus> {
  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. return .failure(
  136. GRPCStatus(
  137. code: .notFound,
  138. message: "The provided file or a dependency of the provided file could not be found."
  139. )
  140. )
  141. }
  142. visited.insert(currentFileName)
  143. }
  144. return .success(serializedFileDescriptorProtos)
  145. }
  146. internal func nameOfFileContainingSymbol(named symbolName: String) -> Result<String, GRPCStatus> {
  147. guard let fileName = self.fileNameBySymbol[symbolName] else {
  148. return .failure(
  149. GRPCStatus(
  150. code: .notFound,
  151. message: "The provided symbol could not be found."
  152. )
  153. )
  154. }
  155. return .success(fileName)
  156. }
  157. internal func nameOfFileContainingExtension(
  158. extendeeName: String,
  159. fieldNumber number: Int32
  160. ) -> Result<String, GRPCStatus> {
  161. let key = ExtensionDescriptor(extendeeTypeName: extendeeName, fieldNumber: number)
  162. guard let fileName = self.fileNameByExtensionDescriptor[key] else {
  163. return .failure(
  164. GRPCStatus(
  165. code: .notFound,
  166. message: "The provided extension could not be found."
  167. )
  168. )
  169. }
  170. return .success(fileName)
  171. }
  172. // Returns an empty array if the type has no extensions.
  173. internal func extensionsFieldNumbersOfType(
  174. named typeName: String
  175. ) -> Result<[Int32], GRPCStatus> {
  176. guard let fieldNumbers = self.fieldNumbersByType[typeName] else {
  177. return .failure(
  178. GRPCStatus(
  179. code: .invalidArgument,
  180. message: "The provided type is invalid."
  181. )
  182. )
  183. }
  184. return .success(fieldNumbers)
  185. }
  186. }
  187. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  188. internal final class ReflectionServiceProvider: Grpc_Reflection_V1_ServerReflectionAsyncProvider {
  189. private let protoRegistry: ReflectionServiceData
  190. internal init(fileDescriptorProtos: [Google_Protobuf_FileDescriptorProto]) throws {
  191. self.protoRegistry = try ReflectionServiceData(
  192. fileDescriptors: fileDescriptorProtos
  193. )
  194. }
  195. internal func _findFileByFileName(
  196. _ fileName: String
  197. ) -> Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, GRPCStatus> {
  198. return self.protoRegistry
  199. .serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
  200. .map { fileDescriptorProtos in
  201. Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse.fileDescriptorResponse(
  202. .with {
  203. $0.fileDescriptorProto = fileDescriptorProtos
  204. }
  205. )
  206. }
  207. }
  208. internal func findFileByFileName(
  209. _ fileName: String,
  210. request: Grpc_Reflection_V1_ServerReflectionRequest
  211. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  212. let result = self._findFileByFileName(fileName)
  213. return result.makeResponse(request: request)
  214. }
  215. internal func getServicesNames(
  216. request: Grpc_Reflection_V1_ServerReflectionRequest
  217. ) throws -> Grpc_Reflection_V1_ServerReflectionResponse {
  218. var listServicesResponse = Grpc_Reflection_V1_ListServiceResponse()
  219. listServicesResponse.service = self.protoRegistry.serviceNames.map { serviceName in
  220. Grpc_Reflection_V1_ServiceResponse.with {
  221. $0.name = serviceName
  222. }
  223. }
  224. return Grpc_Reflection_V1_ServerReflectionResponse(
  225. request: request,
  226. messageResponse: .listServicesResponse(listServicesResponse)
  227. )
  228. }
  229. internal func findFileBySymbol(
  230. _ symbolName: String,
  231. request: Grpc_Reflection_V1_ServerReflectionRequest
  232. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  233. let result = self.protoRegistry.nameOfFileContainingSymbol(
  234. named: symbolName
  235. ).flatMap {
  236. self._findFileByFileName($0)
  237. }
  238. return result.makeResponse(request: request)
  239. }
  240. internal func findFileByExtension(
  241. extensionRequest: Grpc_Reflection_V1_ExtensionRequest,
  242. request: Grpc_Reflection_V1_ServerReflectionRequest
  243. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  244. let result = self.protoRegistry.nameOfFileContainingExtension(
  245. extendeeName: extensionRequest.containingType,
  246. fieldNumber: extensionRequest.extensionNumber
  247. ).flatMap {
  248. self._findFileByFileName($0)
  249. }
  250. return result.makeResponse(request: request)
  251. }
  252. internal func findExtensionsFieldNumbersOfType(
  253. named typeName: String,
  254. request: Grpc_Reflection_V1_ServerReflectionRequest
  255. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  256. let result = self.protoRegistry.extensionsFieldNumbersOfType(
  257. named: typeName
  258. ).map { fieldNumbers in
  259. Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse.allExtensionNumbersResponse(
  260. Grpc_Reflection_V1_ExtensionNumberResponse.with {
  261. $0.baseTypeName = typeName
  262. $0.extensionNumber = fieldNumbers
  263. }
  264. )
  265. }
  266. return result.makeResponse(request: request)
  267. }
  268. internal func serverReflectionInfo(
  269. requestStream: GRPCAsyncRequestStream<Grpc_Reflection_V1_ServerReflectionRequest>,
  270. responseStream: GRPCAsyncResponseStreamWriter<Grpc_Reflection_V1_ServerReflectionResponse>,
  271. context: GRPCAsyncServerCallContext
  272. ) async throws {
  273. for try await request in requestStream {
  274. switch request.messageRequest {
  275. case let .fileByFilename(fileName):
  276. let response = self.findFileByFileName(
  277. fileName,
  278. request: request
  279. )
  280. try await responseStream.send(response)
  281. case .listServices:
  282. let response = try self.getServicesNames(request: request)
  283. try await responseStream.send(response)
  284. case let .fileContainingSymbol(symbolName):
  285. let response = self.findFileBySymbol(
  286. symbolName,
  287. request: request
  288. )
  289. try await responseStream.send(response)
  290. case let .fileContainingExtension(extensionRequest):
  291. let response = self.findFileByExtension(
  292. extensionRequest: extensionRequest,
  293. request: request
  294. )
  295. try await responseStream.send(response)
  296. case let .allExtensionNumbersOfType(typeName):
  297. let response = self.findExtensionsFieldNumbersOfType(
  298. named: typeName,
  299. request: request
  300. )
  301. try await responseStream.send(response)
  302. default:
  303. let response = Grpc_Reflection_V1_ServerReflectionResponse(
  304. request: request,
  305. messageResponse: .errorResponse(
  306. Grpc_Reflection_V1_ErrorResponse.with {
  307. $0.errorCode = Int32(GRPCStatus.Code.unimplemented.rawValue)
  308. $0.errorMessage = "The request is not implemented."
  309. }
  310. )
  311. )
  312. try await responseStream.send(response)
  313. }
  314. }
  315. }
  316. }
  317. extension Grpc_Reflection_V1_ServerReflectionResponse {
  318. init(
  319. request: Grpc_Reflection_V1_ServerReflectionRequest,
  320. messageResponse: Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse
  321. ) {
  322. self = .with {
  323. $0.validHost = request.host
  324. $0.originalRequest = request
  325. $0.messageResponse = messageResponse
  326. }
  327. }
  328. }
  329. extension Google_Protobuf_FileDescriptorProto {
  330. var qualifiedServiceAndMethodNames: [String] {
  331. var names: [String] = []
  332. for service in self.service {
  333. names.append(self.package + "." + service.name)
  334. names.append(
  335. contentsOf: service.method
  336. .map { self.package + "." + service.name + "." + $0.name }
  337. )
  338. }
  339. return names
  340. }
  341. var qualifiedMessageTypes: [String] {
  342. return self.messageType.map {
  343. self.package + "." + $0.name
  344. }
  345. }
  346. var qualifiedEnumTypes: [String] {
  347. return self.enumType.map {
  348. self.package + "." + $0.name
  349. }
  350. }
  351. var qualifiedSymbolNames: [String] {
  352. var names = self.qualifiedServiceAndMethodNames
  353. names.append(contentsOf: self.qualifiedMessageTypes)
  354. names.append(contentsOf: self.qualifiedEnumTypes)
  355. return names
  356. }
  357. }
  358. extension Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, GRPCStatus> {
  359. func recover() -> Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, Never>
  360. {
  361. self.flatMapError { status in
  362. let error = Grpc_Reflection_V1_ErrorResponse.with {
  363. $0.errorCode = Int32(status.code.rawValue)
  364. $0.errorMessage = status.message ?? ""
  365. }
  366. return .success(.errorResponse(error))
  367. }
  368. }
  369. func makeResponse(
  370. request: Grpc_Reflection_V1_ServerReflectionRequest
  371. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  372. let result = self.recover().attachRequest(request)
  373. // Safe to '!' as the failure type is 'Never'.
  374. return try! result.get()
  375. }
  376. }
  377. extension Result
  378. where Success == Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse {
  379. func attachRequest(
  380. _ request: Grpc_Reflection_V1_ServerReflectionRequest
  381. ) -> Result<Grpc_Reflection_V1_ServerReflectionResponse, Failure> {
  382. self.map { message in
  383. Grpc_Reflection_V1_ServerReflectionResponse(request: request, messageResponse: message)
  384. }
  385. }
  386. }