ReflectionService.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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. /// Creates a `ReflectionService` by loading serialized reflection data created by `protoc-gen-grpc-swift`.
  27. ///
  28. /// You can generate serialized reflection data using the `protoc-gen-grpc-swift` plugin for `protoc` by
  29. /// setting the `ReflectionData` option to `True`. The paths provided should be absolute or relative to the
  30. /// current working directory.
  31. ///
  32. /// - Parameter filePaths: The paths to files containing serialized reflection data.
  33. ///
  34. /// - Throws: When a file can't be read from disk or parsed.
  35. public init(serializedFileDescriptorProtoFilePaths filePaths: [String]) throws {
  36. let fileDescriptorProtos = try ReflectionService.readSerializedFileDescriptorProtos(
  37. atPaths: filePaths
  38. )
  39. self.reflectionService = try ReflectionServiceProvider(
  40. fileDescriptorProtos: fileDescriptorProtos
  41. )
  42. }
  43. public init(fileDescriptors: [Google_Protobuf_FileDescriptorProto]) throws {
  44. self.reflectionService = try ReflectionServiceProvider(fileDescriptorProtos: fileDescriptors)
  45. }
  46. public func handle(
  47. method name: Substring,
  48. context: GRPC.CallHandlerContext
  49. ) -> GRPC.GRPCServerHandlerProtocol? {
  50. self.reflectionService.handle(method: name, context: context)
  51. }
  52. }
  53. internal struct ReflectionServiceData: Sendable {
  54. internal struct FileDescriptorProtoData: Sendable {
  55. internal var serializedFileDescriptorProto: Data
  56. internal var dependencyFileNames: [String]
  57. }
  58. private struct ExtensionDescriptor: Sendable, Hashable {
  59. internal let extendeeTypeName: String
  60. internal let fieldNumber: Int32
  61. }
  62. internal var fileDescriptorDataByFilename: [String: FileDescriptorProtoData]
  63. internal var serviceNames: [String]
  64. internal var fileNameBySymbol: [String: String]
  65. // Stores the file names for each extension identified by an ExtensionDescriptor object.
  66. private var fileNameByExtensionDescriptor: [ExtensionDescriptor: String]
  67. // Stores the field numbers for each type that has extensions.
  68. private var fieldNumbersByType: [String: [Int32]]
  69. internal init(fileDescriptors: [Google_Protobuf_FileDescriptorProto]) throws {
  70. self.serviceNames = []
  71. self.fileDescriptorDataByFilename = [:]
  72. self.fileNameBySymbol = [:]
  73. self.fileNameByExtensionDescriptor = [:]
  74. self.fieldNumbersByType = [:]
  75. for fileDescriptorProto in fileDescriptors {
  76. let serializedFileDescriptorProto: Data
  77. do {
  78. serializedFileDescriptorProto = try fileDescriptorProto.serializedData()
  79. } catch {
  80. throw GRPCStatus(
  81. code: .invalidArgument,
  82. message:
  83. "The \(fileDescriptorProto.name) could not be serialized."
  84. )
  85. }
  86. let protoData = FileDescriptorProtoData(
  87. serializedFileDescriptorProto: serializedFileDescriptorProto,
  88. dependencyFileNames: fileDescriptorProto.dependency
  89. )
  90. self.fileDescriptorDataByFilename[fileDescriptorProto.name] = protoData
  91. self.serviceNames.append(
  92. contentsOf: fileDescriptorProto.service.map { fileDescriptorProto.package + "." + $0.name }
  93. )
  94. // Populating the <symbol, file name> dictionary.
  95. for qualifiedSybolName in fileDescriptorProto.qualifiedSymbolNames {
  96. let oldValue = self.fileNameBySymbol.updateValue(
  97. fileDescriptorProto.name,
  98. forKey: qualifiedSybolName
  99. )
  100. if let oldValue = oldValue {
  101. throw GRPCStatus(
  102. code: .alreadyExists,
  103. message:
  104. "The \(qualifiedSybolName) symbol from \(fileDescriptorProto.name) already exists in \(oldValue)."
  105. )
  106. }
  107. }
  108. for typeName in fileDescriptorProto.qualifiedMessageTypes {
  109. self.fieldNumbersByType[typeName] = []
  110. }
  111. // Populating the <extension descriptor, file name> dictionary and the <typeName, [FieldNumber]> one.
  112. for `extension` in fileDescriptorProto.extension {
  113. let typeName = String(`extension`.extendee.drop(while: { $0 == "." }))
  114. let extensionDescriptor = ExtensionDescriptor(
  115. extendeeTypeName: typeName,
  116. fieldNumber: `extension`.number
  117. )
  118. let oldFileName = self.fileNameByExtensionDescriptor.updateValue(
  119. fileDescriptorProto.name,
  120. forKey: extensionDescriptor
  121. )
  122. if let oldFileName = oldFileName {
  123. throw GRPCStatus(
  124. code: .alreadyExists,
  125. message:
  126. """
  127. The extension of the \(extensionDescriptor.extendeeTypeName) type with the field number equal to \
  128. \(extensionDescriptor.fieldNumber) from \(fileDescriptorProto.name) already exists in \(oldFileName).
  129. """
  130. )
  131. }
  132. self.fieldNumbersByType[typeName, default: []].append(`extension`.number)
  133. }
  134. }
  135. }
  136. internal func serialisedFileDescriptorProtosForDependenciesOfFile(
  137. named fileName: String
  138. ) -> Result<[Data], GRPCStatus> {
  139. var toVisit = Deque<String>()
  140. var visited = Set<String>()
  141. var serializedFileDescriptorProtos: [Data] = []
  142. toVisit.append(fileName)
  143. while let currentFileName = toVisit.popFirst() {
  144. if let protoData = self.fileDescriptorDataByFilename[currentFileName] {
  145. toVisit.append(
  146. contentsOf: protoData.dependencyFileNames
  147. .filter { name in
  148. return !visited.contains(name)
  149. }
  150. )
  151. let serializedFileDescriptorProto = protoData.serializedFileDescriptorProto
  152. serializedFileDescriptorProtos.append(serializedFileDescriptorProto)
  153. } else {
  154. return .failure(
  155. GRPCStatus(
  156. code: .notFound,
  157. message: "The provided file or a dependency of the provided file could not be found."
  158. )
  159. )
  160. }
  161. visited.insert(currentFileName)
  162. }
  163. return .success(serializedFileDescriptorProtos)
  164. }
  165. internal func nameOfFileContainingSymbol(named symbolName: String) -> Result<String, GRPCStatus> {
  166. guard let fileName = self.fileNameBySymbol[symbolName] else {
  167. return .failure(
  168. GRPCStatus(
  169. code: .notFound,
  170. message: "The provided symbol could not be found."
  171. )
  172. )
  173. }
  174. return .success(fileName)
  175. }
  176. internal func nameOfFileContainingExtension(
  177. extendeeName: String,
  178. fieldNumber number: Int32
  179. ) -> Result<String, GRPCStatus> {
  180. let key = ExtensionDescriptor(extendeeTypeName: extendeeName, fieldNumber: number)
  181. guard let fileName = self.fileNameByExtensionDescriptor[key] else {
  182. return .failure(
  183. GRPCStatus(
  184. code: .notFound,
  185. message: "The provided extension could not be found."
  186. )
  187. )
  188. }
  189. return .success(fileName)
  190. }
  191. // Returns an empty array if the type has no extensions.
  192. internal func extensionsFieldNumbersOfType(
  193. named typeName: String
  194. ) -> Result<[Int32], GRPCStatus> {
  195. guard let fieldNumbers = self.fieldNumbersByType[typeName] else {
  196. return .failure(
  197. GRPCStatus(
  198. code: .invalidArgument,
  199. message: "The provided type is invalid."
  200. )
  201. )
  202. }
  203. return .success(fieldNumbers)
  204. }
  205. }
  206. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  207. internal final class ReflectionServiceProvider: Grpc_Reflection_V1_ServerReflectionAsyncProvider {
  208. private let protoRegistry: ReflectionServiceData
  209. internal init(fileDescriptorProtos: [Google_Protobuf_FileDescriptorProto]) throws {
  210. self.protoRegistry = try ReflectionServiceData(
  211. fileDescriptors: fileDescriptorProtos
  212. )
  213. }
  214. internal func _findFileByFileName(
  215. _ fileName: String
  216. ) -> Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, GRPCStatus> {
  217. return self.protoRegistry
  218. .serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
  219. .map { fileDescriptorProtos in
  220. Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse.fileDescriptorResponse(
  221. .with {
  222. $0.fileDescriptorProto = fileDescriptorProtos
  223. }
  224. )
  225. }
  226. }
  227. internal func findFileByFileName(
  228. _ fileName: String,
  229. request: Grpc_Reflection_V1_ServerReflectionRequest
  230. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  231. let result = self._findFileByFileName(fileName)
  232. return result.makeResponse(request: request)
  233. }
  234. internal func getServicesNames(
  235. request: Grpc_Reflection_V1_ServerReflectionRequest
  236. ) throws -> Grpc_Reflection_V1_ServerReflectionResponse {
  237. var listServicesResponse = Grpc_Reflection_V1_ListServiceResponse()
  238. listServicesResponse.service = self.protoRegistry.serviceNames.map { serviceName in
  239. Grpc_Reflection_V1_ServiceResponse.with {
  240. $0.name = serviceName
  241. }
  242. }
  243. return Grpc_Reflection_V1_ServerReflectionResponse(
  244. request: request,
  245. messageResponse: .listServicesResponse(listServicesResponse)
  246. )
  247. }
  248. internal func findFileBySymbol(
  249. _ symbolName: String,
  250. request: Grpc_Reflection_V1_ServerReflectionRequest
  251. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  252. let result = self.protoRegistry.nameOfFileContainingSymbol(
  253. named: symbolName
  254. ).flatMap {
  255. self._findFileByFileName($0)
  256. }
  257. return result.makeResponse(request: request)
  258. }
  259. internal func findFileByExtension(
  260. extensionRequest: Grpc_Reflection_V1_ExtensionRequest,
  261. request: Grpc_Reflection_V1_ServerReflectionRequest
  262. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  263. let result = self.protoRegistry.nameOfFileContainingExtension(
  264. extendeeName: extensionRequest.containingType,
  265. fieldNumber: extensionRequest.extensionNumber
  266. ).flatMap {
  267. self._findFileByFileName($0)
  268. }
  269. return result.makeResponse(request: request)
  270. }
  271. internal func findExtensionsFieldNumbersOfType(
  272. named typeName: String,
  273. request: Grpc_Reflection_V1_ServerReflectionRequest
  274. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  275. let result = self.protoRegistry.extensionsFieldNumbersOfType(
  276. named: typeName
  277. ).map { fieldNumbers in
  278. Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse.allExtensionNumbersResponse(
  279. Grpc_Reflection_V1_ExtensionNumberResponse.with {
  280. $0.baseTypeName = typeName
  281. $0.extensionNumber = fieldNumbers
  282. }
  283. )
  284. }
  285. return result.makeResponse(request: request)
  286. }
  287. internal func serverReflectionInfo(
  288. requestStream: GRPCAsyncRequestStream<Grpc_Reflection_V1_ServerReflectionRequest>,
  289. responseStream: GRPCAsyncResponseStreamWriter<Grpc_Reflection_V1_ServerReflectionResponse>,
  290. context: GRPCAsyncServerCallContext
  291. ) async throws {
  292. for try await request in requestStream {
  293. switch request.messageRequest {
  294. case let .fileByFilename(fileName):
  295. let response = self.findFileByFileName(
  296. fileName,
  297. request: request
  298. )
  299. try await responseStream.send(response)
  300. case .listServices:
  301. let response = try self.getServicesNames(request: request)
  302. try await responseStream.send(response)
  303. case let .fileContainingSymbol(symbolName):
  304. let response = self.findFileBySymbol(
  305. symbolName,
  306. request: request
  307. )
  308. try await responseStream.send(response)
  309. case let .fileContainingExtension(extensionRequest):
  310. let response = self.findFileByExtension(
  311. extensionRequest: extensionRequest,
  312. request: request
  313. )
  314. try await responseStream.send(response)
  315. case let .allExtensionNumbersOfType(typeName):
  316. let response = self.findExtensionsFieldNumbersOfType(
  317. named: typeName,
  318. request: request
  319. )
  320. try await responseStream.send(response)
  321. default:
  322. let response = Grpc_Reflection_V1_ServerReflectionResponse(
  323. request: request,
  324. messageResponse: .errorResponse(
  325. Grpc_Reflection_V1_ErrorResponse.with {
  326. $0.errorCode = Int32(GRPCStatus.Code.unimplemented.rawValue)
  327. $0.errorMessage = "The request is not implemented."
  328. }
  329. )
  330. )
  331. try await responseStream.send(response)
  332. }
  333. }
  334. }
  335. }
  336. extension Grpc_Reflection_V1_ServerReflectionResponse {
  337. init(
  338. request: Grpc_Reflection_V1_ServerReflectionRequest,
  339. messageResponse: Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse
  340. ) {
  341. self = .with {
  342. $0.validHost = request.host
  343. $0.originalRequest = request
  344. $0.messageResponse = messageResponse
  345. }
  346. }
  347. }
  348. extension Google_Protobuf_FileDescriptorProto {
  349. var qualifiedServiceAndMethodNames: [String] {
  350. var names: [String] = []
  351. for service in self.service {
  352. names.append(self.package + "." + service.name)
  353. names.append(
  354. contentsOf: service.method
  355. .map { self.package + "." + service.name + "." + $0.name }
  356. )
  357. }
  358. return names
  359. }
  360. var qualifiedMessageTypes: [String] {
  361. return self.messageType.map {
  362. self.package + "." + $0.name
  363. }
  364. }
  365. var qualifiedEnumTypes: [String] {
  366. return self.enumType.map {
  367. self.package + "." + $0.name
  368. }
  369. }
  370. var qualifiedSymbolNames: [String] {
  371. var names = self.qualifiedServiceAndMethodNames
  372. names.append(contentsOf: self.qualifiedMessageTypes)
  373. names.append(contentsOf: self.qualifiedEnumTypes)
  374. return names
  375. }
  376. }
  377. extension Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, GRPCStatus> {
  378. func recover() -> Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, Never>
  379. {
  380. self.flatMapError { status in
  381. let error = Grpc_Reflection_V1_ErrorResponse.with {
  382. $0.errorCode = Int32(status.code.rawValue)
  383. $0.errorMessage = status.message ?? ""
  384. }
  385. return .success(.errorResponse(error))
  386. }
  387. }
  388. func makeResponse(
  389. request: Grpc_Reflection_V1_ServerReflectionRequest
  390. ) -> Grpc_Reflection_V1_ServerReflectionResponse {
  391. let result = self.recover().attachRequest(request)
  392. // Safe to '!' as the failure type is 'Never'.
  393. return try! result.get()
  394. }
  395. }
  396. extension Result
  397. where Success == Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse {
  398. func attachRequest(
  399. _ request: Grpc_Reflection_V1_ServerReflectionRequest
  400. ) -> Result<Grpc_Reflection_V1_ServerReflectionResponse, Failure> {
  401. self.map { message in
  402. Grpc_Reflection_V1_ServerReflectionResponse(request: request, messageResponse: message)
  403. }
  404. }
  405. }
  406. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  407. extension ReflectionService {
  408. static func readSerializedFileDescriptorProto(
  409. atPath path: String
  410. ) throws -> Google_Protobuf_FileDescriptorProto {
  411. let fileURL: URL
  412. #if os(Linux)
  413. fileURL = URL(fileURLWithPath: path)
  414. #else
  415. if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) {
  416. fileURL = URL(filePath: path, directoryHint: .notDirectory)
  417. } else {
  418. fileURL = URL(fileURLWithPath: path)
  419. }
  420. #endif
  421. let binaryData = try Data(contentsOf: fileURL)
  422. guard let serializedData = Data(base64Encoded: binaryData) else {
  423. throw GRPCStatus(
  424. code: .invalidArgument,
  425. message:
  426. """
  427. The \(path) file contents could not be transformed \
  428. into serialized data representing a file descriptor proto.
  429. """
  430. )
  431. }
  432. return try Google_Protobuf_FileDescriptorProto(serializedData: serializedData)
  433. }
  434. static func readSerializedFileDescriptorProtos(
  435. atPaths paths: [String]
  436. ) throws -> [Google_Protobuf_FileDescriptorProto] {
  437. var fileDescriptorProtos = [Google_Protobuf_FileDescriptorProto]()
  438. fileDescriptorProtos.reserveCapacity(paths.count)
  439. for path in paths {
  440. try fileDescriptorProtos.append(readSerializedFileDescriptorProto(atPath: path))
  441. }
  442. return fileDescriptorProtos
  443. }
  444. }