HTTP2TransportTests.swift 49 KB

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