HTTP2TransportTLSEnabledTests.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /*
  2. * Copyright 2025, 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 Foundation
  17. import GRPCCore
  18. import GRPCNIOTransportHTTP2Posix
  19. import GRPCNIOTransportHTTP2TransportServices
  20. import NIOSSL
  21. import SwiftASN1
  22. import Testing
  23. #if canImport(Network)
  24. import Network
  25. #endif
  26. @Suite("HTTP/2 transport E2E tests with TLS enabled")
  27. struct HTTP2TransportTLSEnabledTests {
  28. // - MARK: Tests
  29. @Test(
  30. "When using defaults, server does not perform client verification",
  31. arguments: TransportKind.clientsWithTLS,
  32. TransportKind.serversWithTLS
  33. )
  34. @available(gRPCSwiftNIOTransport 2.0, *)
  35. func testRPC_Defaults_OK(
  36. clientTransport: TransportKind,
  37. serverTransport: TransportKind
  38. ) async throws {
  39. let certificateKeyPairs = try SelfSignedCertificateKeyPairs()
  40. let clientConfig = self.makeDefaultTLSClientConfig(
  41. for: clientTransport,
  42. certificateKeyPairs: certificateKeyPairs
  43. )
  44. let serverConfig = self.makeDefaultTLSServerConfig(
  45. for: serverTransport,
  46. certificateKeyPairs: certificateKeyPairs
  47. )
  48. try await self.withClientAndServer(
  49. clientConfig: clientConfig,
  50. serverConfig: serverConfig
  51. ) { control in
  52. await #expect(throws: Never.self) {
  53. try await self.executeUnaryRPC(control: control)
  54. }
  55. }
  56. }
  57. @available(gRPCSwiftNIOTransport 2.0, *)
  58. final class TransportSpecificInterceptor: ServerInterceptor {
  59. let clientCert: [UInt8]
  60. init(_ clientCert: [UInt8]) {
  61. self.clientCert = clientCert
  62. }
  63. func intercept<Input, Output>(
  64. request: GRPCCore.StreamingServerRequest<Input>,
  65. context: GRPCCore.ServerContext,
  66. next:
  67. @Sendable (GRPCCore.StreamingServerRequest<Input>, GRPCCore.ServerContext) async throws
  68. -> GRPCCore.StreamingServerResponse<Output>
  69. ) async throws -> GRPCCore.StreamingServerResponse<Output>
  70. where Input: Sendable, Output: Sendable {
  71. let transportSpecific = context.transportSpecific
  72. let transportSpecificAsPosixContext = try #require(
  73. transportSpecific as? HTTP2ServerTransport.Posix.Context
  74. )
  75. let peerCertificate = try #require(transportSpecificAsPosixContext.peerCertificate)
  76. var derSerializer = DER.Serializer()
  77. try peerCertificate.serialize(into: &derSerializer)
  78. #expect(derSerializer.serializedBytes == self.clientCert)
  79. return try await next(request, context)
  80. }
  81. }
  82. @Test(
  83. "Using the mTLS defaults, and with Posix transport, validate we get the peer cert on the server",
  84. arguments: [TransportKind.posix]
  85. )
  86. @available(gRPCSwiftNIOTransport 2.0, *)
  87. func testRPC_mTLS_TransportContext_OK(supportedTransport: TransportKind) async throws {
  88. let certificateKeyPairs = try SelfSignedCertificateKeyPairs()
  89. let clientConfig = self.makeMTLSClientConfig(
  90. for: supportedTransport,
  91. certificateKeyPairs: certificateKeyPairs,
  92. serverHostname: "localhost"
  93. )
  94. let serverConfig = self.makeMTLSServerConfig(
  95. for: supportedTransport,
  96. certificateKeyPairs: certificateKeyPairs,
  97. includeClientCertificateInTrustRoots: true
  98. )
  99. try await self.withClientAndServer(
  100. clientConfig: clientConfig,
  101. serverConfig: serverConfig,
  102. interceptors: [TransportSpecificInterceptor(certificateKeyPairs.client.certificate)]
  103. ) { control in
  104. await #expect(throws: Never.self) {
  105. try await self.executeUnaryRPC(control: control)
  106. }
  107. }
  108. }
  109. @Test(
  110. "When using mTLS defaults, both client and server verify each others' certificates",
  111. arguments: TransportKind.clientsWithTLS,
  112. TransportKind.clientsWithTLS
  113. )
  114. @available(gRPCSwiftNIOTransport 2.0, *)
  115. func testRPC_mTLS_OK(
  116. clientTransport: TransportKind,
  117. serverTransport: TransportKind
  118. ) async throws {
  119. let certificateKeyPairs = try SelfSignedCertificateKeyPairs()
  120. let clientConfig = self.makeMTLSClientConfig(
  121. for: clientTransport,
  122. certificateKeyPairs: certificateKeyPairs,
  123. serverHostname: "localhost"
  124. )
  125. let serverConfig = self.makeMTLSServerConfig(
  126. for: serverTransport,
  127. certificateKeyPairs: certificateKeyPairs,
  128. includeClientCertificateInTrustRoots: true
  129. )
  130. try await self.withClientAndServer(
  131. clientConfig: clientConfig,
  132. serverConfig: serverConfig
  133. ) { control in
  134. await #expect(throws: Never.self) {
  135. try await self.executeUnaryRPC(control: control)
  136. }
  137. }
  138. }
  139. @Test(
  140. "When using mTLS with PEM files, both client and server verify each others' certificates"
  141. )
  142. @available(gRPCSwiftNIOTransport 2.0, *)
  143. func testRPC_mTLS_posixFileBasedCertificates_OK() async throws {
  144. // Create a new certificate chain that has 4 certificate/key pairs: root, intermediate, client, server
  145. let certificateChain = try CertificateChain()
  146. // Tag our certificate files with the function name
  147. let filePaths = try certificateChain.writeToTemp()
  148. // Check that the files
  149. #expect(FileManager.default.fileExists(atPath: filePaths.clientCert))
  150. #expect(FileManager.default.fileExists(atPath: filePaths.clientKey))
  151. #expect(FileManager.default.fileExists(atPath: filePaths.serverCert))
  152. #expect(FileManager.default.fileExists(atPath: filePaths.serverKey))
  153. #expect(FileManager.default.fileExists(atPath: filePaths.trustRoots))
  154. // Create configurations
  155. let clientConfig = self.makeMTLSClientConfig(
  156. certificatePath: filePaths.clientCert,
  157. keyPath: filePaths.clientKey,
  158. trustRootsPath: filePaths.trustRoots,
  159. serverHostname: CertificateChain.serverName
  160. )
  161. let serverConfig = self.makeMTLSServerConfig(
  162. certificatePath: filePaths.serverCert,
  163. keyPath: filePaths.serverKey,
  164. trustRootsPath: filePaths.trustRoots
  165. )
  166. // Run the test
  167. try await self.withClientAndServer(
  168. clientConfig: clientConfig,
  169. serverConfig: serverConfig
  170. ) { control in
  171. await #expect(throws: Never.self) {
  172. try await self.executeUnaryRPC(control: control)
  173. }
  174. }
  175. }
  176. @Test(
  177. "Error is surfaced when client fails server verification",
  178. arguments: TransportKind.clientsWithTLS,
  179. TransportKind.clientsWithTLS
  180. )
  181. @available(gRPCSwiftNIOTransport 2.0, *)
  182. // Verification should fail because the custom hostname is missing on the client.
  183. func testClientFailsServerValidation(
  184. clientTransport: TransportKind,
  185. serverTransport: TransportKind
  186. ) async throws {
  187. let certificateKeyPairs = try SelfSignedCertificateKeyPairs()
  188. let clientTransportConfig = self.makeDefaultTLSClientConfig(
  189. for: clientTransport,
  190. certificateKeyPairs: certificateKeyPairs,
  191. authority: "wrong-hostname"
  192. )
  193. let serverTransportConfig = self.makeDefaultTLSServerConfig(
  194. for: serverTransport,
  195. certificateKeyPairs: certificateKeyPairs
  196. )
  197. await #expect {
  198. try await self.withClientAndServer(
  199. clientConfig: clientTransportConfig,
  200. serverConfig: serverTransportConfig
  201. ) { control in
  202. try await self.executeUnaryRPC(control: control)
  203. }
  204. } throws: { error in
  205. let rootError = try #require(error as? RPCError)
  206. #expect(rootError.code == .unavailable)
  207. switch clientTransport {
  208. case .posix:
  209. #expect(
  210. rootError.message
  211. == "The server accepted the TCP connection but closed the connection before completing the HTTP/2 connection preface."
  212. )
  213. let sslError = try #require(rootError.cause as? NIOSSLExtraError)
  214. guard sslError == .failedToValidateHostname else {
  215. Issue.record(
  216. "Should be a NIOSSLExtraError.failedToValidateHostname error, but was: \(String(describing: rootError.cause))"
  217. )
  218. return false
  219. }
  220. #if canImport(Network)
  221. case .transportServices:
  222. #expect(rootError.message.starts(with: "Could not establish a connection to"))
  223. let nwError = try #require(rootError.cause as? NWError)
  224. guard case .tls(Security.errSSLBadCert) = nwError else {
  225. Issue.record(
  226. "Should be a NWError.tls(-9808/errSSLBadCert) error, but was: \(String(describing: rootError.cause))"
  227. )
  228. return false
  229. }
  230. #endif
  231. case .wrappedChannel:
  232. fatalError("Unsupported")
  233. }
  234. return true
  235. }
  236. }
  237. @Test(
  238. "Error is surfaced when server fails client verification",
  239. arguments: TransportKind.clientsWithTLS,
  240. TransportKind.clientsWithTLS
  241. )
  242. @available(gRPCSwiftNIOTransport 2.0, *)
  243. // Verification should fail because the client does not offer a cert that
  244. // the server can use for mutual verification.
  245. func testServerFailsClientValidation(
  246. clientTransport: TransportKind,
  247. serverTransport: TransportKind
  248. ) async throws {
  249. let certificateKeyPairs = try SelfSignedCertificateKeyPairs()
  250. let clientTransportConfig = self.makeDefaultTLSClientConfig(
  251. for: clientTransport,
  252. certificateKeyPairs: certificateKeyPairs
  253. )
  254. let serverTransportConfig = self.makeMTLSServerConfig(
  255. for: serverTransport,
  256. certificateKeyPairs: certificateKeyPairs,
  257. includeClientCertificateInTrustRoots: true
  258. )
  259. await #expect {
  260. try await self.withClientAndServer(
  261. clientConfig: clientTransportConfig,
  262. serverConfig: serverTransportConfig
  263. ) { control in
  264. try await self.executeUnaryRPC(control: control)
  265. }
  266. } throws: { error in
  267. let rootError = try #require(error as? RPCError)
  268. #expect(rootError.code == .unavailable)
  269. #expect(
  270. rootError.message
  271. == "The server accepted the TCP connection but closed the connection before completing the HTTP/2 connection preface."
  272. )
  273. switch clientTransport {
  274. case .posix:
  275. let sslError = try #require(rootError.cause as? NIOSSL.BoringSSLError)
  276. guard case .sslError = sslError else {
  277. Issue.record(
  278. "Should be a NIOSSL.sslError error, but was: \(String(describing: rootError.cause))"
  279. )
  280. return false
  281. }
  282. #if canImport(Network)
  283. case .transportServices:
  284. let nwError = try #require(rootError.cause as? NWError)
  285. guard case .tls(Security.errSSLPeerCertUnknown) = nwError else {
  286. // When the TLS handshake fails, the connection will be closed from the client.
  287. // Network.framework will generally surface the right SSL error (in this case, an "unknown
  288. // certificate" from the server), but it will sometimes instead return the broken pipe
  289. // error caused by the underlying TLS handshake handler closing the connection:
  290. // we should tolerate this.
  291. if case .posix(POSIXErrorCode.EPIPE) = nwError {
  292. return true
  293. }
  294. Issue.record(
  295. "Should be a NWError.tls(-9829/errSSLPeerCertUnknown) error, but was: \(String(describing: rootError.cause))"
  296. )
  297. return false
  298. }
  299. #endif
  300. case .wrappedChannel:
  301. fatalError("Unsupported")
  302. }
  303. return true
  304. }
  305. }
  306. // - MARK: Test Utilities
  307. enum TLSEnabledTestsError: Error {
  308. case failedToImportPKCS12
  309. case unexpectedListeningAddress
  310. }
  311. struct Config<Transport, Security> {
  312. var security: Security
  313. var transport: Transport
  314. }
  315. @available(gRPCSwiftNIOTransport 2.0, *)
  316. enum ClientConfig {
  317. typealias Posix = Config<
  318. HTTP2ClientTransport.Posix.Config,
  319. HTTP2ClientTransport.Posix.TransportSecurity
  320. >
  321. case posix(Posix)
  322. #if canImport(Network)
  323. typealias TransportServices = Config<
  324. HTTP2ClientTransport.TransportServices.Config,
  325. HTTP2ClientTransport.TransportServices.TransportSecurity
  326. >
  327. case transportServices(TransportServices)
  328. #endif
  329. }
  330. @available(gRPCSwiftNIOTransport 2.0, *)
  331. enum ServerConfig {
  332. typealias Posix = Config<
  333. HTTP2ServerTransport.Posix.Config,
  334. HTTP2ServerTransport.Posix.TransportSecurity
  335. >
  336. case posix(Posix)
  337. #if canImport(Network)
  338. typealias TransportServices = Config<
  339. HTTP2ServerTransport.TransportServices.Config,
  340. HTTP2ServerTransport.TransportServices.TransportSecurity
  341. >
  342. case transportServices(TransportServices)
  343. #endif
  344. }
  345. @available(gRPCSwiftNIOTransport 2.0, *)
  346. private func makeDefaultPlaintextPosixClientConfig() -> ClientConfig.Posix {
  347. ClientConfig.Posix(
  348. security: .plaintext,
  349. transport: .defaults { config in
  350. config.backoff.initial = .milliseconds(100)
  351. config.backoff.multiplier = 1
  352. config.backoff.jitter = 0
  353. }
  354. )
  355. }
  356. #if canImport(Network)
  357. @available(gRPCSwiftNIOTransport 2.0, *)
  358. private func makeDefaultPlaintextTSClientConfig() -> ClientConfig.TransportServices {
  359. ClientConfig.TransportServices(
  360. security: .plaintext,
  361. transport: .defaults { config in
  362. config.backoff.initial = .milliseconds(100)
  363. config.backoff.multiplier = 1
  364. config.backoff.jitter = 0
  365. }
  366. )
  367. }
  368. #endif
  369. @available(gRPCSwiftNIOTransport 2.0, *)
  370. private func makeDefaultTLSClientConfig(
  371. for transportSecurity: TransportKind,
  372. certificateKeyPairs: SelfSignedCertificateKeyPairs,
  373. authority: String? = "localhost"
  374. ) -> ClientConfig {
  375. switch transportSecurity {
  376. case .posix:
  377. var config = self.makeDefaultPlaintextPosixClientConfig()
  378. config.security = .tls {
  379. $0.trustRoots = .certificates([
  380. .bytes(certificateKeyPairs.server.certificate, format: .der)
  381. ])
  382. }
  383. config.transport.http2.authority = authority
  384. return .posix(config)
  385. #if canImport(Network)
  386. case .transportServices:
  387. var config = self.makeDefaultPlaintextTSClientConfig()
  388. config.security = .tls {
  389. $0.trustRoots = .certificates([
  390. .bytes(certificateKeyPairs.server.certificate, format: .der)
  391. ])
  392. }
  393. config.transport.http2.authority = authority
  394. return .transportServices(config)
  395. #endif
  396. case .wrappedChannel:
  397. fatalError("Unsupported")
  398. }
  399. }
  400. #if canImport(Network)
  401. @available(gRPCSwiftNIOTransport 2.0, *)
  402. private func makeSecIdentityProvider(
  403. certificateBytes: [UInt8],
  404. privateKeyBytes: [UInt8]
  405. ) throws -> SecIdentity {
  406. let password = "somepassword"
  407. let bundle = NIOSSLPKCS12Bundle(
  408. certificateChain: [try NIOSSLCertificate(bytes: certificateBytes, format: .der)],
  409. privateKey: try NIOSSLPrivateKey(bytes: privateKeyBytes, format: .der)
  410. )
  411. let pkcs12Bytes = try bundle.serialize(passphrase: password.utf8)
  412. let options =
  413. [
  414. kSecImportExportPassphrase as String: password,
  415. kSecImportToMemoryOnly: kCFBooleanTrue!,
  416. ] as [AnyHashable: Any]
  417. var rawItems: CFArray?
  418. let status = SecPKCS12Import(
  419. Data(pkcs12Bytes) as CFData,
  420. options as CFDictionary,
  421. &rawItems
  422. )
  423. guard status == errSecSuccess else {
  424. Issue.record("Failed to import PKCS12 bundle: status \(status).")
  425. throw TLSEnabledTestsError.failedToImportPKCS12
  426. }
  427. let items = rawItems! as! [[String: Any]]
  428. let firstItem = items[0]
  429. let identity = firstItem[kSecImportItemIdentity as String] as! SecIdentity
  430. return identity
  431. }
  432. #endif
  433. @available(gRPCSwiftNIOTransport 2.0, *)
  434. private func makeMTLSClientConfig(
  435. for transportKind: TransportKind,
  436. certificateKeyPairs: SelfSignedCertificateKeyPairs,
  437. serverHostname: String?
  438. ) -> ClientConfig {
  439. switch transportKind {
  440. case .posix:
  441. var config = self.makeDefaultPlaintextPosixClientConfig()
  442. config.security = .mTLS(
  443. certificateChain: [.bytes(certificateKeyPairs.client.certificate, format: .der)],
  444. privateKey: .bytes(certificateKeyPairs.client.key, format: .der)
  445. ) {
  446. $0.trustRoots = .certificates([
  447. .bytes(certificateKeyPairs.server.certificate, format: .der)
  448. ])
  449. }
  450. config.transport.http2.authority = serverHostname
  451. return .posix(config)
  452. #if canImport(Network)
  453. case .transportServices:
  454. var config = self.makeDefaultPlaintextTSClientConfig()
  455. config.security = .mTLS {
  456. try self.makeSecIdentityProvider(
  457. certificateBytes: certificateKeyPairs.client.certificate,
  458. privateKeyBytes: certificateKeyPairs.client.key
  459. )
  460. } configure: {
  461. $0.trustRoots = .certificates([
  462. .bytes(certificateKeyPairs.server.certificate, format: .der)
  463. ])
  464. }
  465. config.transport.http2.authority = serverHostname
  466. return .transportServices(config)
  467. #endif
  468. case .wrappedChannel:
  469. fatalError("Unsupported")
  470. }
  471. }
  472. @available(gRPCSwiftNIOTransport 2.0, *)
  473. private func makeMTLSClientConfig(
  474. certificatePath: String,
  475. keyPath: String,
  476. trustRootsPath: String,
  477. serverHostname: String?
  478. ) -> ClientConfig {
  479. var config = self.makeDefaultPlaintextPosixClientConfig()
  480. config.security = .mTLS(
  481. certificateChain: [.file(path: certificatePath, format: .pem)],
  482. privateKey: .file(path: keyPath, format: .pem)
  483. ) {
  484. $0.trustRoots = .certificates([
  485. .file(path: trustRootsPath, format: .pem)
  486. ])
  487. }
  488. config.transport.http2.authority = serverHostname
  489. return .posix(config)
  490. }
  491. @available(gRPCSwiftNIOTransport 2.0, *)
  492. private func makeDefaultPlaintextPosixServerConfig() -> ServerConfig.Posix {
  493. ServerConfig.Posix(security: .plaintext, transport: .defaults)
  494. }
  495. #if canImport(Network)
  496. @available(gRPCSwiftNIOTransport 2.0, *)
  497. private func makeDefaultPlaintextTSServerConfig() -> ServerConfig.TransportServices {
  498. ServerConfig.TransportServices(security: .plaintext, transport: .defaults)
  499. }
  500. #endif
  501. @available(gRPCSwiftNIOTransport 2.0, *)
  502. private func makeDefaultTLSServerConfig(
  503. for transportKind: TransportKind,
  504. certificateKeyPairs: SelfSignedCertificateKeyPairs
  505. ) -> ServerConfig {
  506. switch transportKind {
  507. case .posix:
  508. var config = self.makeDefaultPlaintextPosixServerConfig()
  509. config.security = .tls(
  510. certificateChain: [.bytes(certificateKeyPairs.server.certificate, format: .der)],
  511. privateKey: .bytes(certificateKeyPairs.server.key, format: .der)
  512. )
  513. return .posix(config)
  514. #if canImport(Network)
  515. case .transportServices:
  516. var config = self.makeDefaultPlaintextTSServerConfig()
  517. config.security = .tls {
  518. try self.makeSecIdentityProvider(
  519. certificateBytes: certificateKeyPairs.server.certificate,
  520. privateKeyBytes: certificateKeyPairs.server.key
  521. )
  522. }
  523. return .transportServices(config)
  524. #endif
  525. case .wrappedChannel:
  526. fatalError("Unsupported")
  527. }
  528. }
  529. @available(gRPCSwiftNIOTransport 2.0, *)
  530. private func makeMTLSServerConfig(
  531. for transportKind: TransportKind,
  532. certificateKeyPairs: SelfSignedCertificateKeyPairs,
  533. includeClientCertificateInTrustRoots: Bool
  534. ) -> ServerConfig {
  535. switch transportKind {
  536. case .posix:
  537. var config = self.makeDefaultPlaintextPosixServerConfig()
  538. config.security = .mTLS(
  539. certificateChain: [.bytes(certificateKeyPairs.server.certificate, format: .der)],
  540. privateKey: .bytes(certificateKeyPairs.server.key, format: .der)
  541. ) {
  542. if includeClientCertificateInTrustRoots {
  543. $0.trustRoots = .certificates([
  544. .bytes(certificateKeyPairs.client.certificate, format: .der)
  545. ])
  546. }
  547. }
  548. return .posix(config)
  549. #if canImport(Network)
  550. case .transportServices:
  551. var config = self.makeDefaultPlaintextTSServerConfig()
  552. config.security = .mTLS {
  553. try self.makeSecIdentityProvider(
  554. certificateBytes: certificateKeyPairs.server.certificate,
  555. privateKeyBytes: certificateKeyPairs.server.key
  556. )
  557. } configure: {
  558. if includeClientCertificateInTrustRoots {
  559. $0.trustRoots = .certificates([
  560. .bytes(certificateKeyPairs.client.certificate, format: .der)
  561. ])
  562. }
  563. }
  564. return .transportServices(config)
  565. #endif
  566. case .wrappedChannel:
  567. fatalError("Unsupported")
  568. }
  569. }
  570. @available(gRPCSwiftNIOTransport 2.0, *)
  571. private func makeMTLSServerConfig(
  572. certificatePath: String,
  573. keyPath: String,
  574. trustRootsPath: String
  575. ) -> ServerConfig {
  576. var config = self.makeDefaultPlaintextPosixServerConfig()
  577. config.security = .mTLS(
  578. certificateChain: [.file(path: certificatePath, format: .pem)],
  579. privateKey: .file(path: keyPath, format: .pem)
  580. ) {
  581. $0.trustRoots = .certificates([
  582. .file(path: trustRootsPath, format: .pem)
  583. ])
  584. }
  585. return .posix(config)
  586. }
  587. @available(gRPCSwiftNIOTransport 2.0, *)
  588. func withClientAndServer(
  589. clientConfig: ClientConfig,
  590. serverConfig: ServerConfig,
  591. interceptors: [any ServerInterceptor] = [],
  592. _ test: (ControlClient<NIOClientTransport>) async throws -> Void
  593. ) async throws {
  594. let serverTransport: NIOServerTransport
  595. switch serverConfig {
  596. case .posix(let posix):
  597. serverTransport = NIOServerTransport(
  598. .http2NIOPosix(
  599. address: .ipv4(host: "127.0.0.1", port: 0),
  600. transportSecurity: posix.security,
  601. config: posix.transport
  602. )
  603. )
  604. #if canImport(Network)
  605. case .transportServices(let config):
  606. serverTransport = NIOServerTransport(
  607. .http2NIOTS(
  608. address: .ipv4(host: "127.0.0.1", port: 0),
  609. transportSecurity: config.security,
  610. config: config.transport
  611. )
  612. )
  613. #endif
  614. }
  615. try await withGRPCServer(
  616. transport: serverTransport,
  617. services: [ControlService()],
  618. interceptors: interceptors
  619. ) { server in
  620. guard let address = try await server.listeningAddress?.ipv4 else {
  621. throw TLSEnabledTestsError.unexpectedListeningAddress
  622. }
  623. let target: any ResolvableTarget = .ipv4(host: address.host, port: address.port)
  624. let clientTransport: NIOClientTransport
  625. switch clientConfig {
  626. case .posix(let config):
  627. clientTransport = try NIOClientTransport(
  628. .http2NIOPosix(
  629. target: target,
  630. transportSecurity: config.security,
  631. config: config.transport
  632. )
  633. )
  634. #if canImport(Network)
  635. case .transportServices(let config):
  636. clientTransport = try NIOClientTransport(
  637. .http2NIOTS(target: target, transportSecurity: config.security, config: config.transport)
  638. )
  639. #endif
  640. }
  641. try await withGRPCClient(transport: clientTransport) { client in
  642. let control = ControlClient(wrapping: client)
  643. try await test(control)
  644. }
  645. }
  646. }
  647. @available(gRPCSwiftNIOTransport 2.0, *)
  648. private func executeUnaryRPC(control: ControlClient<NIOClientTransport>) async throws {
  649. let input = ControlInput.with { $0.numberOfMessages = 1 }
  650. let request = ClientRequest(message: input)
  651. try await control.unary(request: request) { response in
  652. _ = #expect(throws: Never.self) {
  653. try response.message
  654. }
  655. }
  656. }
  657. }