ReflectionService.swift 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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. internal var fileDescriptorDataByFilename: [String: FileDescriptorProtoData]
  42. internal var serviceNames: [String]
  43. internal var fileNameBySymbol: [String: String]
  44. internal init(fileDescriptors: [Google_Protobuf_FileDescriptorProto]) throws {
  45. self.serviceNames = []
  46. self.fileDescriptorDataByFilename = [:]
  47. self.fileNameBySymbol = [:]
  48. for fileDescriptorProto in fileDescriptors {
  49. let serializedFileDescriptorProto: Data
  50. do {
  51. serializedFileDescriptorProto = try fileDescriptorProto.serializedData()
  52. } catch {
  53. throw GRPCStatus(
  54. code: .invalidArgument,
  55. message:
  56. "The \(fileDescriptorProto.name) could not be serialized."
  57. )
  58. }
  59. let protoData = FileDescriptorProtoData(
  60. serializedFileDescriptorProto: serializedFileDescriptorProto,
  61. dependencyFileNames: fileDescriptorProto.dependency
  62. )
  63. self.fileDescriptorDataByFilename[fileDescriptorProto.name] = protoData
  64. self.serviceNames.append(contentsOf: fileDescriptorProto.service.map { $0.name })
  65. for qualifiedSybolName in fileDescriptorProto.qualifiedSymbolNames {
  66. let oldValue = self.fileNameBySymbol.updateValue(
  67. fileDescriptorProto.name,
  68. forKey: qualifiedSybolName
  69. )
  70. if let oldValue = oldValue {
  71. throw GRPCStatus(
  72. code: .alreadyExists,
  73. message:
  74. "The \(qualifiedSybolName) symbol from \(fileDescriptorProto.name) already exists in \(oldValue)."
  75. )
  76. }
  77. }
  78. }
  79. }
  80. internal func serialisedFileDescriptorProtosForDependenciesOfFile(
  81. named fileName: String
  82. ) throws -> [Data] {
  83. var toVisit = Deque<String>()
  84. var visited = Set<String>()
  85. var serializedFileDescriptorProtos: [Data] = []
  86. toVisit.append(fileName)
  87. while let currentFileName = toVisit.popFirst() {
  88. if let protoData = self.fileDescriptorDataByFilename[currentFileName] {
  89. toVisit.append(
  90. contentsOf: protoData.dependencyFileNames
  91. .filter { name in
  92. return !visited.contains(name)
  93. }
  94. )
  95. let serializedFileDescriptorProto = protoData.serializedFileDescriptorProto
  96. serializedFileDescriptorProtos.append(serializedFileDescriptorProto)
  97. } else {
  98. throw GRPCStatus(
  99. code: .notFound,
  100. message: "The provided file or a dependency of the provided file could not be found."
  101. )
  102. }
  103. visited.insert(currentFileName)
  104. }
  105. return serializedFileDescriptorProtos
  106. }
  107. internal func nameOfFileContainingSymbol(named symbolName: String) -> String? {
  108. return self.fileNameBySymbol[symbolName]
  109. }
  110. }
  111. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  112. internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsyncProvider {
  113. private let protoRegistry: ReflectionServiceData
  114. internal init(fileDescriptorProtos: [Google_Protobuf_FileDescriptorProto]) throws {
  115. self.protoRegistry = try ReflectionServiceData(
  116. fileDescriptors: fileDescriptorProtos
  117. )
  118. }
  119. internal func findFileByFileName(
  120. _ fileName: String,
  121. request: Reflection_ServerReflectionRequest
  122. ) throws -> Reflection_ServerReflectionResponse {
  123. return Reflection_ServerReflectionResponse(
  124. request: request,
  125. fileDescriptorResponse: try .with {
  126. $0.fileDescriptorProto = try self.protoRegistry
  127. .serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
  128. }
  129. )
  130. }
  131. internal func getServicesNames(
  132. request: Reflection_ServerReflectionRequest
  133. ) throws -> Reflection_ServerReflectionResponse {
  134. var listServicesResponse = Reflection_ListServiceResponse()
  135. listServicesResponse.service = self.protoRegistry.serviceNames.map { serviceName in
  136. Reflection_ServiceResponse.with {
  137. $0.name = serviceName
  138. }
  139. }
  140. return Reflection_ServerReflectionResponse(
  141. request: request,
  142. listServicesResponse: listServicesResponse
  143. )
  144. }
  145. internal func findFileBySymbol(
  146. _ symbolName: String,
  147. request: Reflection_ServerReflectionRequest
  148. ) throws -> Reflection_ServerReflectionResponse {
  149. guard let fileName = self.protoRegistry.nameOfFileContainingSymbol(named: symbolName) else {
  150. throw GRPCStatus(
  151. code: .notFound,
  152. message: "The provided symbol could not be found."
  153. )
  154. }
  155. return try self.findFileByFileName(fileName, request: request)
  156. }
  157. internal func serverReflectionInfo(
  158. requestStream: GRPCAsyncRequestStream<Reflection_ServerReflectionRequest>,
  159. responseStream: GRPCAsyncResponseStreamWriter<Reflection_ServerReflectionResponse>,
  160. context: GRPCAsyncServerCallContext
  161. ) async throws {
  162. for try await request in requestStream {
  163. switch request.messageRequest {
  164. case let .fileByFilename(fileName):
  165. let response = try self.findFileByFileName(
  166. fileName,
  167. request: request
  168. )
  169. try await responseStream.send(response)
  170. case .listServices:
  171. let response = try self.getServicesNames(request: request)
  172. try await responseStream.send(response)
  173. case let .fileContainingSymbol(symbolName):
  174. let response = try self.findFileBySymbol(
  175. symbolName,
  176. request: request
  177. )
  178. try await responseStream.send(response)
  179. default:
  180. throw GRPCStatus(code: .unimplemented)
  181. }
  182. }
  183. }
  184. }
  185. extension Reflection_ServerReflectionResponse {
  186. init(
  187. request: Reflection_ServerReflectionRequest,
  188. fileDescriptorResponse: Reflection_FileDescriptorResponse
  189. ) {
  190. self = .with {
  191. $0.validHost = request.host
  192. $0.originalRequest = request
  193. $0.fileDescriptorResponse = fileDescriptorResponse
  194. }
  195. }
  196. init(
  197. request: Reflection_ServerReflectionRequest,
  198. listServicesResponse: Reflection_ListServiceResponse
  199. ) {
  200. self = .with {
  201. $0.validHost = request.host
  202. $0.originalRequest = request
  203. $0.listServicesResponse = listServicesResponse
  204. }
  205. }
  206. }
  207. extension Google_Protobuf_FileDescriptorProto {
  208. var qualifiedServiceAndMethodNames: [String] {
  209. var names: [String] = []
  210. for service in self.service {
  211. names.append(self.package + "." + service.name)
  212. names.append(
  213. contentsOf: service.method
  214. .map { self.package + "." + service.name + "." + $0.name }
  215. )
  216. }
  217. return names
  218. }
  219. var qualifiedMessageTypes: [String] {
  220. return self.messageType.map {
  221. self.package + "." + $0.name
  222. }
  223. }
  224. var qualifiedEnumTypes: [String] {
  225. return self.enumType.map {
  226. self.package + "." + $0.name
  227. }
  228. }
  229. var qualifiedSymbolNames: [String] {
  230. var names = self.qualifiedServiceAndMethodNames
  231. names.append(contentsOf: self.qualifiedMessageTypes)
  232. names.append(contentsOf: self.qualifiedEnumTypes)
  233. return names
  234. }
  235. }