HTTP2TransportTests.swift 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492
  1. /*
  2. * Copyright 2024, 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. internal import GRPCCore
  17. private import GRPCHTTP2Core
  18. private import GRPCHTTP2TransportNIOPosix
  19. private import GRPCHTTP2TransportNIOTransportServices
  20. private import GRPCProtobuf
  21. internal import XCTest
  22. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  23. final class HTTP2TransportTests: XCTestCase {
  24. // A combination of client and server transport kinds.
  25. struct Transport: Sendable, CustomStringConvertible {
  26. var server: Kind
  27. var client: Kind
  28. enum Kind: Sendable, CustomStringConvertible {
  29. case posix
  30. case niots
  31. var description: String {
  32. switch self {
  33. case .posix:
  34. return "NIOPosix"
  35. case .niots:
  36. return "NIOTS"
  37. }
  38. }
  39. }
  40. var description: String {
  41. "server=\(self.server) client=\(self.client)"
  42. }
  43. }
  44. func forEachTransportPair(
  45. _ transport: [Transport] = .supported,
  46. enableControlService: Bool = true,
  47. clientCompression: CompressionAlgorithm = .none,
  48. clientEnabledCompression: CompressionAlgorithmSet = .none,
  49. serverCompression: CompressionAlgorithmSet = .none,
  50. _ execute: (ControlClient, Transport) async throws -> Void
  51. ) async throws {
  52. for pair in transport {
  53. try await withThrowingTaskGroup(of: Void.self) { group in
  54. let (server, address) = try await self.runServer(
  55. in: &group,
  56. kind: pair.server,
  57. enableControlService: enableControlService,
  58. compression: serverCompression
  59. )
  60. let target: any ResolvableTarget
  61. if let ipv4 = address.ipv4 {
  62. target = .ipv4(host: ipv4.host, port: ipv4.port)
  63. } else if let ipv6 = address.ipv6 {
  64. target = .ipv6(host: ipv6.host, port: ipv6.port)
  65. } else if let uds = address.unixDomainSocket {
  66. target = .unixDomainSocket(path: uds.path)
  67. } else {
  68. XCTFail("Unexpected address to connect to")
  69. return
  70. }
  71. let client = try self.makeClient(
  72. kind: pair.client,
  73. target: target,
  74. compression: clientCompression,
  75. enabledCompression: clientEnabledCompression
  76. )
  77. group.addTask {
  78. try await client.run()
  79. }
  80. do {
  81. let control = ControlClient(wrapping: client)
  82. try await execute(control, pair)
  83. } catch {
  84. XCTFail("Unexpected error: '\(error)' (\(pair))")
  85. }
  86. server.beginGracefulShutdown()
  87. client.beginGracefulShutdown()
  88. }
  89. }
  90. }
  91. func forEachClientAndHTTPStatusCodeServer(
  92. _ kind: [Transport.Kind] = [.posix],
  93. _ execute: (ControlClient, Transport.Kind) async throws -> Void
  94. ) async throws {
  95. for clientKind in kind {
  96. try await withThrowingTaskGroup(of: Void.self) { group in
  97. let server = HTTP2StatusCodeServer()
  98. group.addTask {
  99. try await server.run()
  100. }
  101. let address = try await server.listeningAddress
  102. let client = try self.makeClient(
  103. kind: clientKind,
  104. target: .ipv4(host: address.host, port: address.port),
  105. compression: .none,
  106. enabledCompression: .none
  107. )
  108. group.addTask {
  109. try await client.run()
  110. }
  111. do {
  112. let control = ControlClient(wrapping: client)
  113. try await execute(control, clientKind)
  114. } catch {
  115. XCTFail("Unexpected error: '\(error)' (\(clientKind))")
  116. }
  117. group.cancelAll()
  118. }
  119. }
  120. }
  121. private func runServer(
  122. in group: inout ThrowingTaskGroup<Void, any Error>,
  123. kind: Transport.Kind,
  124. enableControlService: Bool,
  125. compression: CompressionAlgorithmSet
  126. ) async throws -> (GRPCServer, GRPCHTTP2Core.SocketAddress) {
  127. let services = enableControlService ? [ControlService()] : []
  128. switch kind {
  129. case .posix:
  130. let server = GRPCServer(
  131. transport: .http2NIOPosix(
  132. address: .ipv4(host: "127.0.0.1", port: 0),
  133. config: .defaults(transportSecurity: .plaintext) {
  134. $0.compression.enabledAlgorithms = compression
  135. }
  136. ),
  137. services: services
  138. )
  139. group.addTask {
  140. try await server.serve()
  141. }
  142. let address = try await server.listeningAddress!
  143. return (server, address)
  144. case .niots:
  145. #if canImport(Network)
  146. let server = GRPCServer(
  147. transport: .http2NIOTS(
  148. address: .ipv4(host: "127.0.0.1", port: 0),
  149. config: .defaults {
  150. $0.compression.enabledAlgorithms = compression
  151. }
  152. ),
  153. services: services
  154. )
  155. group.addTask {
  156. try await server.serve()
  157. }
  158. let address = try await server.listeningAddress!
  159. return (server, address)
  160. #else
  161. throw XCTSkip("Transport not supported on this platform")
  162. #endif
  163. }
  164. }
  165. private func makeClient(
  166. kind: Transport.Kind,
  167. target: any ResolvableTarget,
  168. compression: CompressionAlgorithm,
  169. enabledCompression: CompressionAlgorithmSet
  170. ) throws -> GRPCClient {
  171. let transport: any ClientTransport
  172. switch kind {
  173. case .posix:
  174. var serviceConfig = ServiceConfig()
  175. serviceConfig.loadBalancingConfig = [.roundRobin]
  176. transport = try HTTP2ClientTransport.Posix(
  177. target: target,
  178. config: .defaults {
  179. $0.compression.algorithm = compression
  180. $0.compression.enabledAlgorithms = enabledCompression
  181. },
  182. serviceConfig: serviceConfig
  183. )
  184. case .niots:
  185. fatalError("NIOTS isn't supported yet")
  186. }
  187. return GRPCClient(transport: transport)
  188. }
  189. func testUnaryOK() async throws {
  190. // Client sends one message, server sends back metadata, a single message, and an ok status with
  191. // trailing metadata.
  192. try await self.forEachTransportPair { control, pair in
  193. let input = ControlInput.with {
  194. $0.echoMetadataInHeaders = true
  195. $0.echoMetadataInTrailers = true
  196. $0.numberOfMessages = 1
  197. $0.messageParams = .with {
  198. $0.content = 0
  199. $0.size = 1024
  200. }
  201. }
  202. let metadata: Metadata = ["test-key": "test-value"]
  203. let request = ClientRequest.Single(message: input, metadata: metadata)
  204. try await control.unary(request: request) { response in
  205. let message = try response.message
  206. XCTAssertEqual(message.payload, Data(repeating: 0, count: 1024), "\(pair)")
  207. let initial = response.metadata
  208. XCTAssertEqual(Array(initial["echo-test-key"]), ["test-value"], "\(pair)")
  209. let trailing = response.trailingMetadata
  210. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  211. }
  212. }
  213. }
  214. func testUnaryNotOK() async throws {
  215. // Client sends one message, server sends back metadata, a single message, and a non-ok status
  216. // with trailing metadata.
  217. try await self.forEachTransportPair { control, pair in
  218. let input = ControlInput.with {
  219. $0.echoMetadataInTrailers = true
  220. $0.numberOfMessages = 1
  221. $0.messageParams = .with {
  222. $0.content = 0
  223. $0.size = 1024
  224. }
  225. $0.status = .with {
  226. $0.code = .aborted
  227. $0.message = "\(#function)"
  228. }
  229. }
  230. let metadata: Metadata = ["test-key": "test-value"]
  231. let request = ClientRequest.Single(message: input, metadata: metadata)
  232. try await control.unary(request: request) { response in
  233. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  234. XCTAssertEqual(error.code, .aborted)
  235. XCTAssertEqual(error.message, "\(#function)")
  236. let trailing = error.metadata
  237. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  238. }
  239. let trailing = response.trailingMetadata
  240. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  241. }
  242. }
  243. }
  244. func testUnaryRejected() async throws {
  245. // Client sends one message, server sends non-ok status with trailing metadata.
  246. try await self.forEachTransportPair { control, pair in
  247. let metadata: Metadata = ["test-key": "test-value"]
  248. let request = ClientRequest.Single<ControlInput>(
  249. message: .trailersOnly(code: .aborted, message: "\(#function)", echoMetadata: true),
  250. metadata: metadata
  251. )
  252. try await control.unary(request: request) { response in
  253. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  254. XCTAssertEqual(error.code, .aborted, "\(pair)")
  255. XCTAssertEqual(error.message, "\(#function)", "\(pair)")
  256. let trailing = error.metadata
  257. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  258. }
  259. // No initial metadata for trailers-only.
  260. XCTAssertEqual(response.metadata, [:])
  261. let trailing = response.trailingMetadata
  262. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  263. }
  264. }
  265. }
  266. func testClientStreamingOK() async throws {
  267. try await self.forEachTransportPair { control, pair in
  268. let metadata: Metadata = ["test-key": "test-value"]
  269. let request = ClientRequest.Stream(
  270. of: ControlInput.self,
  271. metadata: metadata
  272. ) { writer in
  273. try await writer.write(.echoMetadata)
  274. // Send a few messages which are ignored.
  275. try await writer.write(.noOp)
  276. try await writer.write(.noOp)
  277. try await writer.write(.noOp)
  278. // Send a message.
  279. try await writer.write(.messages(1, repeating: 42, count: 1024))
  280. // ... and the final status.
  281. try await writer.write(.status(code: .ok, message: "", echoMetadata: true))
  282. }
  283. try await control.clientStream(request: request) { response in
  284. let message = try response.message
  285. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  286. let initial = response.metadata
  287. XCTAssertEqual(Array(initial["echo-test-key"]), ["test-value"], "\(pair)")
  288. let trailing = response.trailingMetadata
  289. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  290. }
  291. }
  292. }
  293. func testClientStreamingNotOK() async throws {
  294. try await self.forEachTransportPair { control, pair in
  295. let metadata: Metadata = ["test-key": "test-value"]
  296. let request = ClientRequest.Stream(
  297. of: ControlInput.self,
  298. metadata: metadata
  299. ) { writer in
  300. try await writer.write(.echoMetadata)
  301. // Send a few messages which are ignored.
  302. try await writer.write(.noOp)
  303. try await writer.write(.noOp)
  304. try await writer.write(.noOp)
  305. // Send a message.
  306. try await writer.write(.messages(1, repeating: 42, count: 1024))
  307. // Send the final status.
  308. try await writer.write(.status(code: .aborted, message: "\(#function)", echoMetadata: true))
  309. }
  310. try await control.clientStream(request: request) { response in
  311. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  312. XCTAssertEqual(error.code, .aborted, "\(pair)")
  313. XCTAssertEqual(error.message, "\(#function)", "\(pair)")
  314. let trailing = error.metadata
  315. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  316. }
  317. let initial = response.metadata
  318. XCTAssertEqual(Array(initial["echo-test-key"]), ["test-value"], "\(pair)")
  319. let trailing = response.trailingMetadata
  320. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  321. }
  322. }
  323. }
  324. func testClientStreamingRejected() async throws {
  325. // Client sends one message, server sends non-ok status with trailing metadata.
  326. try await self.forEachTransportPair { control, pair in
  327. let metadata: Metadata = ["test-key": "test-value"]
  328. let request = ClientRequest.Stream(
  329. of: ControlInput.self,
  330. metadata: metadata
  331. ) { writer in
  332. let message: ControlInput = .trailersOnly(
  333. code: .aborted,
  334. message: "\(#function)",
  335. echoMetadata: true
  336. )
  337. try await writer.write(message)
  338. }
  339. try await control.clientStream(request: request) { response in
  340. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  341. XCTAssertEqual(error.code, .aborted, "\(pair)")
  342. XCTAssertEqual(error.message, "\(#function)", "\(pair)")
  343. let trailing = error.metadata
  344. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  345. }
  346. // No initial metadata for trailers-only.
  347. XCTAssertEqual(response.metadata, [:])
  348. let trailing = response.trailingMetadata
  349. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  350. }
  351. }
  352. }
  353. func testServerStreamingOK() async throws {
  354. try await self.forEachTransportPair { control, pair in
  355. let metadata: Metadata = ["test-key": "test-value"]
  356. let input = ControlInput.with {
  357. $0.echoMetadataInHeaders = true
  358. $0.echoMetadataInTrailers = true
  359. $0.numberOfMessages = 5
  360. $0.messageParams = .with {
  361. $0.content = 42
  362. $0.size = 1024
  363. }
  364. }
  365. let request = ClientRequest.Single(message: input, metadata: metadata)
  366. try await control.serverStream(request: request) { response in
  367. switch response.accepted {
  368. case .success(let contents):
  369. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  370. var messagesReceived = 0
  371. for try await part in contents.bodyParts {
  372. switch part {
  373. case .message(let message):
  374. messagesReceived += 1
  375. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024))
  376. case .trailingMetadata(let metadata):
  377. XCTAssertEqual(Array(metadata["echo-test-key"]), ["test-value"], "\(pair)")
  378. }
  379. }
  380. XCTAssertEqual(messagesReceived, 5)
  381. case .failure(let error):
  382. throw error
  383. }
  384. }
  385. }
  386. }
  387. func testServerStreamingEmptyOK() async throws {
  388. try await self.forEachTransportPair { control, pair in
  389. let metadata: Metadata = ["test-key": "test-value"]
  390. // Echo back metadata, but don't send any messages.
  391. let input = ControlInput.with {
  392. $0.echoMetadataInHeaders = true
  393. $0.echoMetadataInTrailers = true
  394. }
  395. let request = ClientRequest.Single(message: input, metadata: metadata)
  396. try await control.serverStream(request: request) { response in
  397. switch response.accepted {
  398. case .success(let contents):
  399. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  400. for try await part in contents.bodyParts {
  401. switch part {
  402. case .message:
  403. XCTFail("Unexpected message")
  404. case .trailingMetadata(let metadata):
  405. XCTAssertEqual(Array(metadata["echo-test-key"]), ["test-value"], "\(pair)")
  406. }
  407. }
  408. case .failure(let error):
  409. throw error
  410. }
  411. }
  412. }
  413. }
  414. func testServerStreamingNotOK() async throws {
  415. try await self.forEachTransportPair { control, pair in
  416. let metadata: Metadata = ["test-key": "test-value"]
  417. let input = ControlInput.with {
  418. $0.echoMetadataInHeaders = true
  419. $0.echoMetadataInTrailers = true
  420. $0.numberOfMessages = 5
  421. $0.messageParams = .with {
  422. $0.content = 42
  423. $0.size = 1024
  424. }
  425. $0.status = .with {
  426. $0.code = .aborted
  427. $0.message = "\(#function)"
  428. }
  429. }
  430. let request = ClientRequest.Single(message: input, metadata: metadata)
  431. try await control.serverStream(request: request) { response in
  432. switch response.accepted {
  433. case .success(let contents):
  434. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  435. var messagesReceived = 0
  436. do {
  437. for try await part in contents.bodyParts {
  438. switch part {
  439. case .message(let message):
  440. messagesReceived += 1
  441. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024))
  442. case .trailingMetadata:
  443. XCTFail("Unexpected trailing metadata, should be provided in RPCError")
  444. }
  445. }
  446. XCTFail("Expected error to be thrown")
  447. } catch let error as RPCError {
  448. XCTAssertEqual(error.code, .aborted)
  449. XCTAssertEqual(error.message, "\(#function)")
  450. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  451. }
  452. XCTAssertEqual(messagesReceived, 5)
  453. case .failure(let error):
  454. throw error
  455. }
  456. }
  457. }
  458. }
  459. func testServerStreamingEmptyNotOK() async throws {
  460. try await self.forEachTransportPair { control, pair in
  461. let metadata: Metadata = ["test-key": "test-value"]
  462. let input = ControlInput.with {
  463. $0.echoMetadataInHeaders = true
  464. $0.echoMetadataInTrailers = true
  465. $0.status = .with {
  466. $0.code = .aborted
  467. $0.message = "\(#function)"
  468. }
  469. }
  470. let request = ClientRequest.Single(message: input, metadata: metadata)
  471. try await control.serverStream(request: request) { response in
  472. switch response.accepted {
  473. case .success(let contents):
  474. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  475. do {
  476. for try await _ in contents.bodyParts {
  477. XCTFail("Unexpected message, \(pair)")
  478. }
  479. XCTFail("Expected error to be thrown")
  480. } catch let error as RPCError {
  481. XCTAssertEqual(error.code, .aborted)
  482. XCTAssertEqual(error.message, "\(#function)")
  483. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  484. }
  485. case .failure(let error):
  486. throw error
  487. }
  488. }
  489. }
  490. }
  491. func testServerStreamingRejected() async throws {
  492. try await self.forEachTransportPair { control, pair in
  493. let metadata: Metadata = ["test-key": "test-value"]
  494. let request = ClientRequest.Single<ControlInput>(
  495. message: .trailersOnly(code: .aborted, message: "\(#function)", echoMetadata: true),
  496. metadata: metadata
  497. )
  498. try await control.serverStream(request: request) { response in
  499. switch response.accepted {
  500. case .success:
  501. XCTFail("Expected RPC to be rejected \(pair)")
  502. case .failure(let error):
  503. XCTAssertEqual(error.code, .aborted, "\(pair)")
  504. XCTAssertEqual(error.message, "\(#function)", "\(pair)")
  505. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  506. }
  507. }
  508. }
  509. }
  510. func testBidiStreamingOK() async throws {
  511. try await self.forEachTransportPair { control, pair in
  512. let metadata: Metadata = ["test-key": "test-value"]
  513. let request = ClientRequest.Stream(
  514. of: ControlInput.self,
  515. metadata: metadata
  516. ) { writer in
  517. try await writer.write(.echoMetadata)
  518. // Send a few messages, each is echo'd back.
  519. try await writer.write(.messages(1, repeating: 42, count: 1024))
  520. try await writer.write(.messages(1, repeating: 42, count: 1024))
  521. try await writer.write(.messages(1, repeating: 42, count: 1024))
  522. // Send the final status.
  523. try await writer.write(.status(code: .ok, message: "", echoMetadata: true))
  524. }
  525. try await control.bidiStream(request: request) { response in
  526. switch response.accepted {
  527. case .success(let contents):
  528. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  529. var messagesReceived = 0
  530. for try await part in contents.bodyParts {
  531. switch part {
  532. case .message(let message):
  533. messagesReceived += 1
  534. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024))
  535. case .trailingMetadata(let metadata):
  536. XCTAssertEqual(Array(metadata["echo-test-key"]), ["test-value"], "\(pair)")
  537. }
  538. }
  539. XCTAssertEqual(messagesReceived, 3)
  540. case .failure(let error):
  541. throw error
  542. }
  543. }
  544. }
  545. }
  546. func testBidiStreamingEmptyOK() async throws {
  547. try await self.forEachTransportPair { control, pair in
  548. let request = ClientRequest.Stream(of: ControlInput.self) { _ in }
  549. try await control.bidiStream(request: request) { response in
  550. switch response.accepted {
  551. case .success(let contents):
  552. var receivedTrailingMetadata = false
  553. for try await part in contents.bodyParts {
  554. switch part {
  555. case .message:
  556. XCTFail("Unexpected message \(pair)")
  557. case .trailingMetadata:
  558. XCTAssertFalse(receivedTrailingMetadata, "\(pair)")
  559. receivedTrailingMetadata = true
  560. }
  561. }
  562. case .failure(let error):
  563. throw error
  564. }
  565. }
  566. }
  567. }
  568. func testBidiStreamingNotOK() async throws {
  569. try await self.forEachTransportPair { control, pair in
  570. let metadata: Metadata = ["test-key": "test-value"]
  571. let request = ClientRequest.Stream(
  572. of: ControlInput.self,
  573. metadata: metadata
  574. ) { writer in
  575. try await writer.write(.echoMetadata)
  576. // Send a few messages, each is echo'd back.
  577. try await writer.write(.messages(1, repeating: 42, count: 1024))
  578. try await writer.write(.messages(1, repeating: 42, count: 1024))
  579. try await writer.write(.messages(1, repeating: 42, count: 1024))
  580. // Send the final status.
  581. try await writer.write(.status(code: .aborted, message: "\(#function)", echoMetadata: true))
  582. }
  583. try await control.bidiStream(request: request) { response in
  584. switch response.accepted {
  585. case .success(let contents):
  586. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  587. var messagesReceived = 0
  588. do {
  589. for try await part in contents.bodyParts {
  590. switch part {
  591. case .message(let message):
  592. messagesReceived += 1
  593. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024))
  594. case .trailingMetadata:
  595. XCTFail("Trailing metadata should be provided by error")
  596. }
  597. }
  598. XCTFail("Should've thrown error \(pair)")
  599. } catch let error as RPCError {
  600. XCTAssertEqual(error.code, .aborted)
  601. XCTAssertEqual(error.message, "\(#function)")
  602. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  603. }
  604. XCTAssertEqual(messagesReceived, 3)
  605. case .failure(let error):
  606. throw error
  607. }
  608. }
  609. }
  610. }
  611. func testBidiStreamingRejected() async throws {
  612. try await self.forEachTransportPair { control, pair in
  613. let metadata: Metadata = ["test-key": "test-value"]
  614. let request = ClientRequest.Stream(
  615. of: ControlInput.self,
  616. metadata: metadata
  617. ) { writer in
  618. try await writer.write(
  619. .trailersOnly(
  620. code: .aborted,
  621. message: "\(#function)",
  622. echoMetadata: true
  623. )
  624. )
  625. }
  626. try await control.bidiStream(request: request) { response in
  627. switch response.accepted {
  628. case .success:
  629. XCTFail("Expected RPC to fail \(pair)")
  630. case .failure(let error):
  631. XCTAssertEqual(error.code, .aborted)
  632. XCTAssertEqual(error.message, "\(#function)")
  633. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"])
  634. }
  635. }
  636. }
  637. }
  638. // MARK: - Not Implemented
  639. func testUnaryNotImplemented() async throws {
  640. try await self.forEachTransportPair(enableControlService: false) { control, pair in
  641. let request = ClientRequest.Single(message: ControlInput())
  642. try await control.unary(request: request) { response in
  643. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  644. XCTAssertEqual(error.code, .unimplemented)
  645. }
  646. }
  647. }
  648. }
  649. func testClientStreamingNotImplemented() async throws {
  650. try await self.forEachTransportPair(enableControlService: false) { control, pair in
  651. let request = ClientRequest.Stream(of: ControlInput.self) { _ in }
  652. try await control.clientStream(request: request) { response in
  653. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  654. XCTAssertEqual(error.code, .unimplemented)
  655. }
  656. }
  657. }
  658. }
  659. func testServerStreamingNotImplemented() async throws {
  660. try await self.forEachTransportPair(enableControlService: false) { control, pair in
  661. let request = ClientRequest.Single(message: ControlInput())
  662. try await control.serverStream(request: request) { response in
  663. XCTAssertThrowsError(ofType: RPCError.self, try response.accepted.get()) { error in
  664. XCTAssertEqual(error.code, .unimplemented)
  665. }
  666. }
  667. }
  668. }
  669. func testBidiStreamingNotImplemented() async throws {
  670. try await self.forEachTransportPair(enableControlService: false) { control, pair in
  671. let request = ClientRequest.Stream(of: ControlInput.self) { _ in }
  672. try await control.bidiStream(request: request) { response in
  673. XCTAssertThrowsError(ofType: RPCError.self, try response.accepted.get()) { error in
  674. XCTAssertEqual(error.code, .unimplemented)
  675. }
  676. }
  677. }
  678. }
  679. // MARK: - Compression tests
  680. private func testUnaryCompression(
  681. client: CompressionAlgorithm,
  682. server: CompressionAlgorithm,
  683. control: ControlClient,
  684. pair: Transport
  685. ) async throws {
  686. let message = ControlInput.with {
  687. $0.echoMetadataInHeaders = true
  688. $0.numberOfMessages = 1
  689. $0.messageParams = .with {
  690. $0.content = 42
  691. $0.size = 1024
  692. }
  693. }
  694. var options = CallOptions.defaults
  695. options.compression = client
  696. try await control.unary(
  697. request: ClientRequest.Single(message: message),
  698. options: options
  699. ) { response in
  700. // Check the client algorithm.
  701. switch client {
  702. case .deflate, .gzip:
  703. // "echo-grpc-encoding" is the value of "grpc-encoding" sent from the client to the server.
  704. let encoding = Array(response.metadata["echo-grpc-encoding"])
  705. XCTAssertEqual(encoding, ["\(client.name)"], "\(pair)")
  706. case .none:
  707. ()
  708. default:
  709. XCTFail("Unhandled compression '\(client)'")
  710. }
  711. // Check the server algorithm.
  712. switch server {
  713. case .deflate, .gzip:
  714. let encoding = Array(response.metadata["grpc-encoding"])
  715. XCTAssertEqual(encoding, ["\(server.name)"], "\(pair)")
  716. case .none:
  717. ()
  718. default:
  719. XCTFail("Unhandled compression '\(client)'")
  720. }
  721. let message = try response.message
  722. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  723. }
  724. }
  725. private func testClientStreamingCompression(
  726. client: CompressionAlgorithm,
  727. server: CompressionAlgorithm,
  728. control: ControlClient,
  729. pair: Transport
  730. ) async throws {
  731. let request = ClientRequest.Stream(of: ControlInput.self) { writer in
  732. try await writer.write(.echoMetadata)
  733. try await writer.write(.noOp)
  734. try await writer.write(.noOp)
  735. try await writer.write(.messages(1, repeating: 42, count: 1024))
  736. }
  737. var options = CallOptions.defaults
  738. options.compression = client
  739. try await control.clientStream(request: request, options: options) { response in
  740. // Check the client algorithm.
  741. switch client {
  742. case .deflate, .gzip:
  743. // "echo-grpc-encoding" is the value of "grpc-encoding" sent from the client to the server.
  744. let encoding = Array(response.metadata["echo-grpc-encoding"])
  745. XCTAssertEqual(encoding, ["\(client.name)"], "\(pair)")
  746. case .none:
  747. ()
  748. default:
  749. XCTFail("Unhandled compression '\(client)'")
  750. }
  751. // Check the server algorithm.
  752. switch server {
  753. case .deflate, .gzip:
  754. let encoding = Array(response.metadata["grpc-encoding"])
  755. XCTAssertEqual(encoding, ["\(server.name)"], "\(pair)")
  756. case .none:
  757. ()
  758. default:
  759. XCTFail("Unhandled compression '\(client)'")
  760. }
  761. let message = try response.message
  762. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  763. }
  764. }
  765. private func testServerStreamingCompression(
  766. client: CompressionAlgorithm,
  767. server: CompressionAlgorithm,
  768. control: ControlClient,
  769. pair: Transport
  770. ) async throws {
  771. let message = ControlInput.with {
  772. $0.echoMetadataInHeaders = true
  773. $0.numberOfMessages = 5
  774. $0.messageParams = .with {
  775. $0.content = 42
  776. $0.size = 1024
  777. }
  778. }
  779. var options = CallOptions.defaults
  780. options.compression = client
  781. try await control.serverStream(
  782. request: ClientRequest.Single(message: message),
  783. options: options
  784. ) { response in
  785. // Check the client algorithm.
  786. switch client {
  787. case .deflate, .gzip:
  788. // "echo-grpc-encoding" is the value of "grpc-encoding" sent from the client to the server.
  789. let encoding = Array(response.metadata["echo-grpc-encoding"])
  790. XCTAssertEqual(encoding, ["\(client.name)"], "\(pair)")
  791. case .none:
  792. ()
  793. default:
  794. XCTFail("Unhandled compression '\(client)'")
  795. }
  796. // Check the server algorithm.
  797. switch server {
  798. case .deflate, .gzip:
  799. let encoding = Array(response.metadata["grpc-encoding"])
  800. XCTAssertEqual(encoding, ["\(server.name)"], "\(pair)")
  801. case .none:
  802. ()
  803. default:
  804. XCTFail("Unhandled compression '\(client)'")
  805. }
  806. for try await message in response.messages {
  807. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  808. }
  809. }
  810. }
  811. private func testBidiStreamingCompression(
  812. client: CompressionAlgorithm,
  813. server: CompressionAlgorithm,
  814. control: ControlClient,
  815. pair: Transport
  816. ) async throws {
  817. let request = ClientRequest.Stream(of: ControlInput.self) { writer in
  818. try await writer.write(.echoMetadata)
  819. try await writer.write(.messages(1, repeating: 42, count: 1024))
  820. try await writer.write(.messages(1, repeating: 42, count: 1024))
  821. try await writer.write(.messages(1, repeating: 42, count: 1024))
  822. }
  823. var options = CallOptions.defaults
  824. options.compression = client
  825. try await control.bidiStream(request: request, options: options) { response in
  826. // Check the client algorithm.
  827. switch client {
  828. case .deflate, .gzip:
  829. // "echo-grpc-encoding" is the value of "grpc-encoding" sent from the client to the server.
  830. let encoding = Array(response.metadata["echo-grpc-encoding"])
  831. XCTAssertEqual(encoding, ["\(client.name)"], "\(pair)")
  832. case .none:
  833. ()
  834. default:
  835. XCTFail("Unhandled compression '\(client)'")
  836. }
  837. // Check the server algorithm.
  838. switch server {
  839. case .deflate, .gzip:
  840. let encoding = Array(response.metadata["grpc-encoding"])
  841. XCTAssertEqual(encoding, ["\(server.name)"], "\(pair)")
  842. case .none:
  843. ()
  844. default:
  845. XCTFail("Unhandled compression '\(client)'")
  846. }
  847. for try await message in response.messages {
  848. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  849. }
  850. }
  851. }
  852. func testUnaryDeflateCompression() async throws {
  853. try await self.forEachTransportPair(
  854. clientCompression: .deflate,
  855. clientEnabledCompression: .deflate,
  856. serverCompression: .deflate
  857. ) { control, pair in
  858. try await self.testUnaryCompression(
  859. client: .deflate,
  860. server: .deflate,
  861. control: control,
  862. pair: pair
  863. )
  864. }
  865. }
  866. func testUnaryGzipCompression() async throws {
  867. try await self.forEachTransportPair(
  868. clientCompression: .gzip,
  869. clientEnabledCompression: .gzip,
  870. serverCompression: .gzip
  871. ) { control, pair in
  872. try await self.testUnaryCompression(
  873. client: .gzip,
  874. server: .gzip,
  875. control: control,
  876. pair: pair
  877. )
  878. }
  879. }
  880. func testClientStreamingDeflateCompression() async throws {
  881. try await self.forEachTransportPair(
  882. clientCompression: .deflate,
  883. clientEnabledCompression: .deflate,
  884. serverCompression: .deflate
  885. ) { control, pair in
  886. try await self.testClientStreamingCompression(
  887. client: .deflate,
  888. server: .deflate,
  889. control: control,
  890. pair: pair
  891. )
  892. }
  893. }
  894. func testClientStreamingGzipCompression() async throws {
  895. try await self.forEachTransportPair(
  896. clientCompression: .gzip,
  897. clientEnabledCompression: .gzip,
  898. serverCompression: .gzip
  899. ) { control, pair in
  900. try await self.testClientStreamingCompression(
  901. client: .gzip,
  902. server: .gzip,
  903. control: control,
  904. pair: pair
  905. )
  906. }
  907. }
  908. func testServerStreamingDeflateCompression() async throws {
  909. try await self.forEachTransportPair(
  910. clientCompression: .deflate,
  911. clientEnabledCompression: .deflate,
  912. serverCompression: .deflate
  913. ) { control, pair in
  914. try await self.testServerStreamingCompression(
  915. client: .deflate,
  916. server: .deflate,
  917. control: control,
  918. pair: pair
  919. )
  920. }
  921. }
  922. func testServerStreamingGzipCompression() async throws {
  923. try await self.forEachTransportPair(
  924. clientCompression: .gzip,
  925. clientEnabledCompression: .gzip,
  926. serverCompression: .gzip
  927. ) { control, pair in
  928. try await self.testServerStreamingCompression(
  929. client: .gzip,
  930. server: .gzip,
  931. control: control,
  932. pair: pair
  933. )
  934. }
  935. }
  936. func testBidiStreamingDeflateCompression() async throws {
  937. try await self.forEachTransportPair(
  938. clientCompression: .deflate,
  939. clientEnabledCompression: .deflate,
  940. serverCompression: .deflate
  941. ) { control, pair in
  942. try await self.testBidiStreamingCompression(
  943. client: .deflate,
  944. server: .deflate,
  945. control: control,
  946. pair: pair
  947. )
  948. }
  949. }
  950. func testBidiStreamingGzipCompression() async throws {
  951. try await self.forEachTransportPair(
  952. clientCompression: .gzip,
  953. clientEnabledCompression: .gzip,
  954. serverCompression: .gzip
  955. ) { control, pair in
  956. try await self.testBidiStreamingCompression(
  957. client: .gzip,
  958. server: .gzip,
  959. control: control,
  960. pair: pair
  961. )
  962. }
  963. }
  964. func testUnaryUnsupportedCompression() async throws {
  965. try await self.forEachTransportPair(
  966. clientEnabledCompression: .all,
  967. serverCompression: .gzip
  968. ) { control, pair in
  969. let message = ControlInput.with {
  970. $0.numberOfMessages = 1
  971. $0.messageParams = .with {
  972. $0.content = 42
  973. $0.size = 1024
  974. }
  975. }
  976. let request = ClientRequest.Single(message: message)
  977. var options = CallOptions.defaults
  978. options.compression = .deflate
  979. try await control.unary(request: request, options: options) { response in
  980. switch response.accepted {
  981. case .success:
  982. XCTFail("RPC should've been rejected")
  983. case .failure(let error):
  984. let acceptEncoding = Array(error.metadata["grpc-accept-encoding"])
  985. // "identity" may or may not be included, so only test for values which must be present.
  986. XCTAssertTrue(acceptEncoding.contains("gzip"))
  987. XCTAssertFalse(acceptEncoding.contains("deflate"))
  988. }
  989. }
  990. }
  991. }
  992. func testClientStreamingUnsupportedCompression() async throws {
  993. try await self.forEachTransportPair(
  994. clientEnabledCompression: .all,
  995. serverCompression: .gzip
  996. ) { control, pair in
  997. let request = ClientRequest.Stream(of: ControlInput.self) { writer in
  998. try await writer.write(.noOp)
  999. }
  1000. var options = CallOptions.defaults
  1001. options.compression = .deflate
  1002. try await control.clientStream(request: request, options: options) { response in
  1003. switch response.accepted {
  1004. case .success:
  1005. XCTFail("RPC should've been rejected")
  1006. case .failure(let error):
  1007. let acceptEncoding = Array(error.metadata["grpc-accept-encoding"])
  1008. // "identity" may or may not be included, so only test for values which must be present.
  1009. XCTAssertTrue(acceptEncoding.contains("gzip"))
  1010. XCTAssertFalse(acceptEncoding.contains("deflate"))
  1011. }
  1012. }
  1013. }
  1014. }
  1015. func testServerStreamingUnsupportedCompression() async throws {
  1016. try await self.forEachTransportPair(
  1017. clientEnabledCompression: .all,
  1018. serverCompression: .gzip
  1019. ) { control, pair in
  1020. let message = ControlInput.with {
  1021. $0.numberOfMessages = 1
  1022. $0.messageParams = .with {
  1023. $0.content = 42
  1024. $0.size = 1024
  1025. }
  1026. }
  1027. let request = ClientRequest.Single(message: message)
  1028. var options = CallOptions.defaults
  1029. options.compression = .deflate
  1030. try await control.serverStream(request: request, options: options) { response in
  1031. switch response.accepted {
  1032. case .success:
  1033. XCTFail("RPC should've been rejected")
  1034. case .failure(let error):
  1035. let acceptEncoding = Array(error.metadata["grpc-accept-encoding"])
  1036. // "identity" may or may not be included, so only test for values which must be present.
  1037. XCTAssertTrue(acceptEncoding.contains("gzip"))
  1038. XCTAssertFalse(acceptEncoding.contains("deflate"))
  1039. }
  1040. }
  1041. }
  1042. }
  1043. func testBidiStreamingUnsupportedCompression() async throws {
  1044. try await self.forEachTransportPair(
  1045. clientEnabledCompression: .all,
  1046. serverCompression: .gzip
  1047. ) { control, pair in
  1048. let request = ClientRequest.Stream(of: ControlInput.self) { writer in
  1049. try await writer.write(.noOp)
  1050. }
  1051. var options = CallOptions.defaults
  1052. options.compression = .deflate
  1053. try await control.bidiStream(request: request, options: options) { response in
  1054. switch response.accepted {
  1055. case .success:
  1056. XCTFail("RPC should've been rejected")
  1057. case .failure(let error):
  1058. let acceptEncoding = Array(error.metadata["grpc-accept-encoding"])
  1059. // "identity" may or may not be included, so only test for values which must be present.
  1060. XCTAssertTrue(acceptEncoding.contains("gzip"))
  1061. XCTAssertFalse(acceptEncoding.contains("deflate"))
  1062. }
  1063. }
  1064. }
  1065. }
  1066. func testUnaryTimeoutPropagatedToServer() async throws {
  1067. try await self.forEachTransportPair { control, pair in
  1068. let message = ControlInput.with {
  1069. $0.echoMetadataInHeaders = true
  1070. $0.numberOfMessages = 1
  1071. }
  1072. let request = ClientRequest.Single(message: message)
  1073. var options = CallOptions.defaults
  1074. options.timeout = .seconds(10)
  1075. try await control.unary(request: request, options: options) { response in
  1076. let timeout = Array(response.metadata["echo-grpc-timeout"])
  1077. XCTAssertEqual(timeout.count, 1)
  1078. }
  1079. }
  1080. }
  1081. func testClientStreamingTimeoutPropagatedToServer() async throws {
  1082. try await self.forEachTransportPair { control, pair in
  1083. let request = ClientRequest.Stream(of: ControlInput.self) { writer in
  1084. let message = ControlInput.with {
  1085. $0.echoMetadataInHeaders = true
  1086. $0.numberOfMessages = 1
  1087. }
  1088. try await writer.write(message)
  1089. }
  1090. var options = CallOptions.defaults
  1091. options.timeout = .seconds(10)
  1092. try await control.clientStream(request: request, options: options) { response in
  1093. let timeout = Array(response.metadata["echo-grpc-timeout"])
  1094. XCTAssertEqual(timeout.count, 1)
  1095. }
  1096. }
  1097. }
  1098. func testServerStreamingTimeoutPropagatedToServer() async throws {
  1099. try await self.forEachTransportPair { control, pair in
  1100. let message = ControlInput.with {
  1101. $0.echoMetadataInHeaders = true
  1102. $0.numberOfMessages = 1
  1103. }
  1104. let request = ClientRequest.Single(message: message)
  1105. var options = CallOptions.defaults
  1106. options.timeout = .seconds(10)
  1107. try await control.serverStream(request: request, options: options) { response in
  1108. let timeout = Array(response.metadata["echo-grpc-timeout"])
  1109. XCTAssertEqual(timeout.count, 1)
  1110. }
  1111. }
  1112. }
  1113. func testBidiStreamingTimeoutPropagatedToServer() async throws {
  1114. try await self.forEachTransportPair { control, pair in
  1115. let request = ClientRequest.Stream(of: ControlInput.self) { writer in
  1116. try await writer.write(.echoMetadata)
  1117. }
  1118. var options = CallOptions.defaults
  1119. options.timeout = .seconds(10)
  1120. try await control.bidiStream(request: request, options: options) { response in
  1121. let timeout = Array(response.metadata["echo-grpc-timeout"])
  1122. XCTAssertEqual(timeout.count, 1)
  1123. }
  1124. }
  1125. }
  1126. private static let httpToStatusCodePairs: [(Int, RPCError.Code)] = [
  1127. // See https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md
  1128. (400, .internalError),
  1129. (401, .unauthenticated),
  1130. (403, .permissionDenied),
  1131. (404, .unimplemented),
  1132. (418, .unknown),
  1133. (429, .unavailable),
  1134. (502, .unavailable),
  1135. (503, .unavailable),
  1136. (504, .unavailable),
  1137. (504, .unavailable),
  1138. ]
  1139. func testUnaryAgainstNonGRPCServer() async throws {
  1140. try await self.forEachClientAndHTTPStatusCodeServer { control, kind in
  1141. for (httpCode, expectedStatus) in Self.httpToStatusCodePairs {
  1142. // Tell the server what to respond with.
  1143. let metadata: Metadata = ["response-status": "\(httpCode)"]
  1144. try await control.unary(
  1145. request: ClientRequest.Single(message: .noOp, metadata: metadata)
  1146. ) { response in
  1147. switch response.accepted {
  1148. case .success:
  1149. XCTFail("RPC should have failed with '\(expectedStatus)'")
  1150. case .failure(let error):
  1151. XCTAssertEqual(error.code, expectedStatus)
  1152. }
  1153. }
  1154. }
  1155. }
  1156. }
  1157. func testClientStreamingAgainstNonGRPCServer() async throws {
  1158. try await self.forEachClientAndHTTPStatusCodeServer { control, kind in
  1159. for (httpCode, expectedStatus) in Self.httpToStatusCodePairs {
  1160. // Tell the server what to respond with.
  1161. let request = ClientRequest.Stream(
  1162. of: ControlInput.self,
  1163. metadata: ["response-status": "\(httpCode)"]
  1164. ) { _ in
  1165. }
  1166. try await control.clientStream(request: request) { response in
  1167. switch response.accepted {
  1168. case .success:
  1169. XCTFail("RPC should have failed with '\(expectedStatus)'")
  1170. case .failure(let error):
  1171. XCTAssertEqual(error.code, expectedStatus)
  1172. }
  1173. }
  1174. }
  1175. }
  1176. }
  1177. func testServerStreamingAgainstNonGRPCServer() async throws {
  1178. try await self.forEachClientAndHTTPStatusCodeServer { control, kind in
  1179. for (httpCode, expectedStatus) in Self.httpToStatusCodePairs {
  1180. // Tell the server what to respond with.
  1181. let metadata: Metadata = ["response-status": "\(httpCode)"]
  1182. try await control.serverStream(
  1183. request: ClientRequest.Single(message: .noOp, metadata: metadata)
  1184. ) { response in
  1185. switch response.accepted {
  1186. case .success:
  1187. XCTFail("RPC should have failed with '\(expectedStatus)'")
  1188. case .failure(let error):
  1189. XCTAssertEqual(error.code, expectedStatus)
  1190. }
  1191. }
  1192. }
  1193. }
  1194. }
  1195. func testBidiStreamingAgainstNonGRPCServer() async throws {
  1196. try await self.forEachClientAndHTTPStatusCodeServer { control, kind in
  1197. for (httpCode, expectedStatus) in Self.httpToStatusCodePairs {
  1198. // Tell the server what to respond with.
  1199. let request = ClientRequest.Stream(
  1200. of: ControlInput.self,
  1201. metadata: ["response-status": "\(httpCode)"]
  1202. ) { _ in
  1203. }
  1204. try await control.bidiStream(request: request) { response in
  1205. switch response.accepted {
  1206. case .success:
  1207. XCTFail("RPC should have failed with '\(expectedStatus)'")
  1208. case .failure(let error):
  1209. XCTAssertEqual(error.code, expectedStatus)
  1210. }
  1211. }
  1212. }
  1213. }
  1214. }
  1215. func testUnaryScheme() async throws {
  1216. try await self.forEachTransportPair { control, pair in
  1217. let input = ControlInput.with {
  1218. $0.echoMetadataInHeaders = true
  1219. $0.numberOfMessages = 1
  1220. }
  1221. let request = ClientRequest.Single(message: input)
  1222. try await control.unary(request: request) { response in
  1223. XCTAssertEqual(Array(response.metadata["echo-scheme"]), ["http"])
  1224. }
  1225. }
  1226. }
  1227. func testServerStreamingScheme() async throws {
  1228. try await self.forEachTransportPair { control, pair in
  1229. let input = ControlInput.with {
  1230. $0.echoMetadataInHeaders = true
  1231. $0.numberOfMessages = 1
  1232. }
  1233. let request = ClientRequest.Single(message: input)
  1234. try await control.serverStream(request: request) { response in
  1235. XCTAssertEqual(Array(response.metadata["echo-scheme"]), ["http"])
  1236. }
  1237. }
  1238. }
  1239. func testClientStreamingScheme() async throws {
  1240. try await self.forEachTransportPair { control, pair in
  1241. let request = ClientRequest.Stream(of: ControlInput.self) { writer in
  1242. let input = ControlInput.with {
  1243. $0.echoMetadataInHeaders = true
  1244. $0.numberOfMessages = 1
  1245. }
  1246. try await writer.write(input)
  1247. }
  1248. try await control.clientStream(request: request) { response in
  1249. XCTAssertEqual(Array(response.metadata["echo-scheme"]), ["http"])
  1250. }
  1251. }
  1252. }
  1253. func testBidiStreamingScheme() async throws {
  1254. try await self.forEachTransportPair { control, pair in
  1255. let request = ClientRequest.Stream(of: ControlInput.self) { writer in
  1256. let input = ControlInput.with {
  1257. $0.echoMetadataInHeaders = true
  1258. $0.numberOfMessages = 1
  1259. }
  1260. try await writer.write(input)
  1261. }
  1262. try await control.bidiStream(request: request) { response in
  1263. XCTAssertEqual(Array(response.metadata["echo-scheme"]), ["http"])
  1264. }
  1265. }
  1266. }
  1267. }
  1268. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  1269. extension [HTTP2TransportTests.Transport] {
  1270. static let supported = [
  1271. HTTP2TransportTests.Transport(server: .posix, client: .posix),
  1272. HTTP2TransportTests.Transport(server: .niots, client: .posix),
  1273. ]
  1274. }
  1275. extension ControlInput {
  1276. fileprivate static let echoMetadata = Self.with {
  1277. $0.echoMetadataInHeaders = true
  1278. }
  1279. fileprivate static let noOp = Self()
  1280. fileprivate static func messages(
  1281. _ numberOfMessages: Int,
  1282. repeating: UInt8,
  1283. count: Int
  1284. ) -> Self {
  1285. return Self.with {
  1286. $0.numberOfMessages = Int32(numberOfMessages)
  1287. $0.messageParams = .with {
  1288. $0.content = UInt32(repeating)
  1289. $0.size = Int32(count)
  1290. }
  1291. }
  1292. }
  1293. fileprivate static func status(
  1294. code: Status.Code,
  1295. message: String,
  1296. echoMetadata: Bool
  1297. ) -> Self {
  1298. return Self.with {
  1299. $0.echoMetadataInTrailers = echoMetadata
  1300. $0.status = .with {
  1301. $0.code = StatusCode(rawValue: code.rawValue)!
  1302. $0.message = message
  1303. }
  1304. }
  1305. }
  1306. fileprivate static func trailersOnly(
  1307. code: Status.Code,
  1308. message: String,
  1309. echoMetadata: Bool
  1310. ) -> Self {
  1311. return Self.with {
  1312. $0.echoMetadataInTrailers = echoMetadata
  1313. $0.isTrailersOnly = true
  1314. $0.status = .with {
  1315. $0.code = StatusCode(rawValue: code.rawValue)!
  1316. $0.message = message
  1317. }
  1318. }
  1319. }
  1320. }
  1321. extension CompressionAlgorithm {
  1322. var name: String {
  1323. switch self {
  1324. case .deflate:
  1325. return "deflate"
  1326. case .gzip:
  1327. return "gzip"
  1328. case .none:
  1329. return "identity"
  1330. default:
  1331. return ""
  1332. }
  1333. }
  1334. }