ReflectionService.swift 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 init(fileDescriptors: [Google_Protobuf_FileDescriptorProto]) throws {
  44. self.serviceNames = []
  45. self.fileDescriptorDataByFilename = [:]
  46. for fileDescriptorProto in fileDescriptors {
  47. let serializedFileDescriptorProto: Data
  48. do {
  49. serializedFileDescriptorProto = try fileDescriptorProto.serializedData()
  50. } catch {
  51. throw GRPCStatus(
  52. code: .invalidArgument,
  53. message:
  54. "The \(fileDescriptorProto.name) could not be serialized."
  55. )
  56. }
  57. let protoData = FileDescriptorProtoData(
  58. serializedFileDescriptorProto: serializedFileDescriptorProto,
  59. dependencyFileNames: fileDescriptorProto.dependency
  60. )
  61. self.fileDescriptorDataByFilename[fileDescriptorProto.name] = protoData
  62. self.serviceNames.append(contentsOf: fileDescriptorProto.service.map { $0.name })
  63. }
  64. }
  65. internal func serialisedFileDescriptorProtosForDependenciesOfFile(
  66. named fileName: String
  67. ) throws -> [Data] {
  68. var toVisit = Deque<String>()
  69. var visited = Set<String>()
  70. var serializedFileDescriptorProtos: [Data] = []
  71. toVisit.append(fileName)
  72. while let currentFileName = toVisit.popFirst() {
  73. if let protoData = self.fileDescriptorDataByFilename[currentFileName] {
  74. toVisit.append(
  75. contentsOf: protoData.dependencyFileNames
  76. .filter { name in
  77. return !visited.contains(name)
  78. }
  79. )
  80. let serializedFileDescriptorProto = protoData.serializedFileDescriptorProto
  81. serializedFileDescriptorProtos.append(serializedFileDescriptorProto)
  82. } else {
  83. throw GRPCStatus(
  84. code: .notFound,
  85. message: "The provided file or a dependency of the provided file could not be found."
  86. )
  87. }
  88. visited.insert(currentFileName)
  89. }
  90. return serializedFileDescriptorProtos
  91. }
  92. }
  93. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  94. internal final class ReflectionServiceProvider: Reflection_ServerReflectionAsyncProvider {
  95. private let protoRegistry: ReflectionServiceData
  96. internal init(fileDescriptorProtos: [Google_Protobuf_FileDescriptorProto]) throws {
  97. self.protoRegistry = try ReflectionServiceData(
  98. fileDescriptors: fileDescriptorProtos
  99. )
  100. }
  101. internal func findFileByFileName(
  102. _ fileName: String,
  103. request: Reflection_ServerReflectionRequest
  104. ) throws -> Reflection_ServerReflectionResponse {
  105. return Reflection_ServerReflectionResponse(
  106. request: request,
  107. fileDescriptorResponse: try .with {
  108. $0.fileDescriptorProto = try self.protoRegistry
  109. .serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
  110. }
  111. )
  112. }
  113. internal func getServicesNames(
  114. request: Reflection_ServerReflectionRequest
  115. ) throws -> Reflection_ServerReflectionResponse {
  116. var listServicesResponse = Reflection_ListServiceResponse()
  117. listServicesResponse.service = self.protoRegistry.serviceNames.map { serviceName in
  118. Reflection_ServiceResponse.with {
  119. $0.name = serviceName
  120. }
  121. }
  122. return Reflection_ServerReflectionResponse(
  123. request: request,
  124. listServicesResponse: listServicesResponse
  125. )
  126. }
  127. internal func serverReflectionInfo(
  128. requestStream: GRPCAsyncRequestStream<Reflection_ServerReflectionRequest>,
  129. responseStream: GRPCAsyncResponseStreamWriter<Reflection_ServerReflectionResponse>,
  130. context: GRPCAsyncServerCallContext
  131. ) async throws {
  132. for try await request in requestStream {
  133. switch request.messageRequest {
  134. case let .fileByFilename(fileName):
  135. let response = try self.findFileByFileName(
  136. fileName,
  137. request: request
  138. )
  139. try await responseStream.send(response)
  140. case .listServices:
  141. let response = try self.getServicesNames(request: request)
  142. try await responseStream.send(response)
  143. default:
  144. throw GRPCStatus(code: .unimplemented)
  145. }
  146. }
  147. }
  148. }
  149. extension Reflection_ServerReflectionResponse {
  150. init(
  151. request: Reflection_ServerReflectionRequest,
  152. fileDescriptorResponse: Reflection_FileDescriptorResponse
  153. ) {
  154. self = .with {
  155. $0.validHost = request.host
  156. $0.originalRequest = request
  157. $0.fileDescriptorResponse = fileDescriptorResponse
  158. }
  159. }
  160. init(
  161. request: Reflection_ServerReflectionRequest,
  162. listServicesResponse: Reflection_ListServiceResponse
  163. ) {
  164. self = .with {
  165. $0.validHost = request.host
  166. $0.originalRequest = request
  167. $0.listServicesResponse = listServicesResponse
  168. }
  169. }
  170. }