HTTP2TransportTests.swift 48 KB

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