idb_companion/SwiftServer/GRPCSwiftServer.swift (111 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import Foundation import GRPC import NIOCore import NIOSSL import NIOPosix import FBControlCore import IDBGRPCSwift @objc final class GRPCSwiftServer : NSObject { private struct TLSCertificates { let certificates: [NIOSSLCertificateSource] let privateKey: NIOSSLPrivateKeySource } private var server: EventLoopFuture<Server>? private let provider: CallHandlerProvider private let logger: FBIDBLogger private let serverConfig: Server.Configuration private let ports: FBIDBPortsConfiguration @objc let completed : FBMutableFuture<NSNull> @objc init(target: FBiOSTarget, commandExecutor: FBIDBCommandExecutor, reporter: FBEventReporter, logger: FBIDBLogger, ports: FBIDBPortsConfiguration) throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 4) let tlsCerts = Self.loadCertificates(portConfiguration: ports, logger: logger) let clientToCppServer = Self.internalCppClient(portConfiguration: ports, certificates: tlsCerts, group: group) let interceptors = CompanionServiceInterceptors() self.provider = CompanionServiceProvider(target: target, commandExecutor: commandExecutor, reporter: reporter, logger: logger, internalCppClient: clientToCppServer, interceptors: interceptors) var serverConfiguration = Server.Configuration.default(target: Self.bindTarget(portConfiguration: ports), eventLoopGroup: group, serviceProviders: [provider]) serverConfiguration.maximumReceiveMessageLength = 16777216 serverConfiguration.tlsConfiguration = tlsCerts.map { GRPCTLSConfiguration.makeServerConfigurationBackedByNIOSSL(certificateChain: $0.certificates, privateKey: $0.privateKey) } serverConfiguration.errorDelegate = GRPCSwiftServerErrorDelegate() self.serverConfig = serverConfiguration self.ports = ports self.completed = FBMutableFuture<NSNull>() self.logger = logger super.init() } @objc func start() -> FBMutableFuture<NSDictionary> { // Start the server and print its address once it has started. let future = FBMutableFuture<NSDictionary>() let server = Server.start(configuration: serverConfig) self.server = server logger.info().log("Starting swift server on port \(ports.grpcSwiftPort)") let tslPath = ports.tlsCertPath as String if !tslPath.isEmpty { logger.info().log("Starting swift server with TLS path \(ports.tlsCertPath)") } server.map(\.channel.localAddress).whenSuccess { [weak self, ports] address in self?.logServerStartup(address: address) future.resolve(withResult: ["grpc_swift_port": Int(ports.grpcSwiftPort)]) } server.flatMap(\.onClose).whenCompleteBlocking(onto: .main) { [completed] _ in completed.resolve(withResult: NSNull()) } return future } private func logServerStartup(address: SocketAddress?) { let message = "Swift server started on " if let address = address { logger.info().log(message + address.description) } else { logger.error().log(message + " unknown address") } } private static func bindTarget(portConfiguration: FBIDBPortsConfiguration) -> BindTarget { return .host("localhost", port: Int(portConfiguration.grpcSwiftPort)) } private static func loadCertificates(portConfiguration: FBIDBPortsConfiguration, logger: FBIDBLogger) -> TLSCertificates? { let tlsPath = portConfiguration.tlsCertPath as String guard !tlsPath.isEmpty else { return nil } let tlsURL = URL(fileURLWithPath: tlsPath) do { let rawCert = try Data(contentsOf: tlsURL) let certificate = try NIOSSLCertificateSource.certificate(.init(bytes: [UInt8](rawCert), format: .pem)) let privateKey = try NIOSSLPrivateKeySource.privateKey(.init(bytes: [UInt8](rawCert), format: .pem)) return TLSCertificates( certificates: [certificate], privateKey: privateKey ) } catch { logger.error().log("Unable to load tls certificate. Error: \(error)") fatalError("Unable to load tls certificate. Error: \(error)") } } private static func internalCppClient(portConfiguration: FBIDBPortsConfiguration, certificates: TLSCertificates?, group: MultiThreadedEventLoopGroup) -> Idb_CompanionServiceAsyncClientProtocol { var config = ClientConnection.Configuration.default(target: .host("localhost", port: Int(portConfiguration.grpcPort)), eventLoopGroup: group) config.tlsConfiguration = certificates.map { var nioConf = TLSConfiguration.makeClientConfiguration() nioConf.certificateChain = $0.certificates nioConf.privateKey = $0.privateKey nioConf.certificateVerification = .none return GRPCTLSConfiguration.makeClientConfigurationBackedByNIOSSL(configuration:nioConf) } // Potentially we could have very large files. To improve speed we removing max capacity // and set max frame size to maximum config.maximumReceiveMessageLength = Int.max config.httpMaxFrameSize = 16_777_215 let connection = ClientConnection(configuration: config) return Idb_CompanionServiceAsyncClient(channel: connection) } }