HTTP2TransportTests.swift 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730
  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 XCTest
  21. final class HTTP2TransportTests: XCTestCase {
  22. // A combination of client and server transport kinds.
  23. struct Transport: Sendable, CustomStringConvertible {
  24. var server: Kind
  25. var client: Kind
  26. enum Kind: Sendable, CustomStringConvertible {
  27. case posix
  28. case niots
  29. var description: String {
  30. switch self {
  31. case .posix:
  32. return "NIOPosix"
  33. case .niots:
  34. return "NIOTS"
  35. }
  36. }
  37. }
  38. var description: String {
  39. "server=\(self.server) client=\(self.client)"
  40. }
  41. }
  42. func forEachTransportPair(
  43. _ transport: [Transport] = [.init(server: .posix, client: .posix)],
  44. serverAddress: SocketAddress = .ipv4(host: "127.0.0.1", port: 0),
  45. enableControlService: Bool = true,
  46. clientCompression: CompressionAlgorithm = .none,
  47. clientEnabledCompression: CompressionAlgorithmSet = .none,
  48. serverCompression: CompressionAlgorithmSet = .none,
  49. _ execute: (ControlClient, GRPCServer, Transport) async throws -> Void
  50. ) async throws {
  51. for pair in transport {
  52. try await withThrowingTaskGroup(of: Void.self) { group in
  53. let (server, address) = try await self.runServer(
  54. in: &group,
  55. address: serverAddress,
  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, server, 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. address: SocketAddress,
  124. kind: Transport.Kind,
  125. enableControlService: Bool,
  126. compression: CompressionAlgorithmSet
  127. ) async throws -> (GRPCServer, GRPCNIOTransportCore.SocketAddress) {
  128. let services = enableControlService ? [ControlService()] : []
  129. switch kind {
  130. case .posix:
  131. let server = GRPCServer(
  132. transport: .http2NIOPosix(
  133. address: address,
  134. transportSecurity: .plaintext,
  135. config: .defaults {
  136. $0.compression.enabledAlgorithms = compression
  137. }
  138. ),
  139. services: services
  140. )
  141. group.addTask {
  142. try await server.serve()
  143. }
  144. let address = try await server.listeningAddress!
  145. return (server, address)
  146. case .niots:
  147. #if canImport(Network)
  148. let server = GRPCServer(
  149. transport: .http2NIOTS(
  150. address: address,
  151. transportSecurity: .plaintext,
  152. config: .defaults {
  153. $0.compression.enabledAlgorithms = compression
  154. }
  155. ),
  156. services: services
  157. )
  158. group.addTask {
  159. try await server.serve()
  160. }
  161. let address = try await server.listeningAddress!
  162. return (server, address)
  163. #else
  164. throw XCTSkip("Transport not supported on this platform")
  165. #endif
  166. }
  167. }
  168. private func makeClient(
  169. kind: Transport.Kind,
  170. target: any ResolvableTarget,
  171. compression: CompressionAlgorithm,
  172. enabledCompression: CompressionAlgorithmSet
  173. ) throws -> GRPCClient {
  174. let transport: any ClientTransport
  175. switch kind {
  176. case .posix:
  177. var serviceConfig = ServiceConfig()
  178. serviceConfig.loadBalancingConfig = [.roundRobin]
  179. transport = try HTTP2ClientTransport.Posix(
  180. target: target,
  181. transportSecurity: .plaintext,
  182. config: .defaults {
  183. $0.compression.algorithm = compression
  184. $0.compression.enabledAlgorithms = enabledCompression
  185. },
  186. serviceConfig: serviceConfig
  187. )
  188. case .niots:
  189. #if canImport(Network)
  190. var serviceConfig = ServiceConfig()
  191. serviceConfig.loadBalancingConfig = [.roundRobin]
  192. transport = try HTTP2ClientTransport.TransportServices(
  193. target: target,
  194. transportSecurity: .plaintext,
  195. config: .defaults {
  196. $0.compression.algorithm = compression
  197. $0.compression.enabledAlgorithms = enabledCompression
  198. },
  199. serviceConfig: serviceConfig
  200. )
  201. #else
  202. throw XCTSkip("Transport not supported on this platform")
  203. #endif
  204. }
  205. return GRPCClient(transport: transport)
  206. }
  207. func testUnaryOK() async throws {
  208. // Client sends one message, server sends back metadata, a single message, and an ok status with
  209. // trailing metadata.
  210. try await self.forEachTransportPair { control, _, pair in
  211. let input = ControlInput.with {
  212. $0.echoMetadataInHeaders = true
  213. $0.echoMetadataInTrailers = true
  214. $0.numberOfMessages = 1
  215. $0.payloadParameters = .with {
  216. $0.content = 0
  217. $0.size = 1024
  218. }
  219. }
  220. let metadata: Metadata = ["test-key": "test-value"]
  221. let request = ClientRequest(message: input, metadata: metadata)
  222. try await control.unary(request: request) { response in
  223. let message = try response.message
  224. XCTAssertEqual(message.payload, Data(repeating: 0, count: 1024), "\(pair)")
  225. let initial = response.metadata
  226. XCTAssertEqual(Array(initial["echo-test-key"]), ["test-value"], "\(pair)")
  227. let trailing = response.trailingMetadata
  228. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  229. }
  230. }
  231. }
  232. func testUnaryNotOK() async throws {
  233. // Client sends one message, server sends back metadata, a single message, and a non-ok status
  234. // with trailing metadata.
  235. try await self.forEachTransportPair { control, _, pair in
  236. let input = ControlInput.with {
  237. $0.echoMetadataInTrailers = true
  238. $0.numberOfMessages = 1
  239. $0.payloadParameters = .with {
  240. $0.content = 0
  241. $0.size = 1024
  242. }
  243. $0.status = .with {
  244. $0.code = .aborted
  245. $0.message = "\(#function)"
  246. }
  247. }
  248. let metadata: Metadata = ["test-key": "test-value"]
  249. let request = ClientRequest(message: input, metadata: metadata)
  250. try await control.unary(request: request) { response in
  251. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  252. XCTAssertEqual(error.code, .aborted)
  253. XCTAssertEqual(error.message, "\(#function)")
  254. let trailing = error.metadata
  255. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  256. }
  257. let trailing = response.trailingMetadata
  258. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  259. }
  260. }
  261. }
  262. func testUnaryRejected() async throws {
  263. // Client sends one message, server sends non-ok status with trailing metadata.
  264. try await self.forEachTransportPair { control, _, pair in
  265. let metadata: Metadata = ["test-key": "test-value"]
  266. let request = ClientRequest<ControlInput>(
  267. message: .trailersOnly(code: .aborted, message: "\(#function)", echoMetadata: true),
  268. metadata: metadata
  269. )
  270. try await control.unary(request: request) { response in
  271. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  272. XCTAssertEqual(error.code, .aborted, "\(pair)")
  273. XCTAssertEqual(error.message, "\(#function)", "\(pair)")
  274. let trailing = error.metadata
  275. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  276. }
  277. // No initial metadata for trailers-only.
  278. XCTAssertEqual(response.metadata, [:])
  279. let trailing = response.trailingMetadata
  280. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  281. }
  282. }
  283. }
  284. func testClientStreamingOK() async throws {
  285. try await self.forEachTransportPair { control, _, pair in
  286. let metadata: Metadata = ["test-key": "test-value"]
  287. let request = StreamingClientRequest(
  288. of: ControlInput.self,
  289. metadata: metadata
  290. ) { writer in
  291. try await writer.write(.echoMetadata)
  292. // Send a few messages which are ignored.
  293. try await writer.write(.noOp)
  294. try await writer.write(.noOp)
  295. try await writer.write(.noOp)
  296. // Send a message.
  297. try await writer.write(.messages(1, repeating: 42, count: 1024))
  298. // ... and the final status.
  299. try await writer.write(.status(code: .ok, message: "", echoMetadata: true))
  300. }
  301. try await control.clientStream(request: request) { response in
  302. let message = try response.message
  303. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  304. let initial = response.metadata
  305. XCTAssertEqual(Array(initial["echo-test-key"]), ["test-value"], "\(pair)")
  306. let trailing = response.trailingMetadata
  307. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  308. }
  309. }
  310. }
  311. func testClientStreamingNotOK() async throws {
  312. try await self.forEachTransportPair { control, _, pair in
  313. let metadata: Metadata = ["test-key": "test-value"]
  314. let request = StreamingClientRequest(
  315. of: ControlInput.self,
  316. metadata: metadata
  317. ) { writer in
  318. try await writer.write(.echoMetadata)
  319. // Send a few messages which are ignored.
  320. try await writer.write(.noOp)
  321. try await writer.write(.noOp)
  322. try await writer.write(.noOp)
  323. // Send a message.
  324. try await writer.write(.messages(1, repeating: 42, count: 1024))
  325. // Send the final status.
  326. try await writer.write(.status(code: .aborted, message: "\(#function)", echoMetadata: true))
  327. }
  328. try await control.clientStream(request: request) { response in
  329. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  330. XCTAssertEqual(error.code, .aborted, "\(pair)")
  331. XCTAssertEqual(error.message, "\(#function)", "\(pair)")
  332. let trailing = error.metadata
  333. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  334. }
  335. let initial = response.metadata
  336. XCTAssertEqual(Array(initial["echo-test-key"]), ["test-value"], "\(pair)")
  337. let trailing = response.trailingMetadata
  338. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  339. }
  340. }
  341. }
  342. func testClientStreamingRejected() async throws {
  343. // Client sends one message, server sends non-ok status with trailing metadata.
  344. try await self.forEachTransportPair { control, _, pair in
  345. let metadata: Metadata = ["test-key": "test-value"]
  346. let request = StreamingClientRequest(
  347. of: ControlInput.self,
  348. metadata: metadata
  349. ) { writer in
  350. let message: ControlInput = .trailersOnly(
  351. code: .aborted,
  352. message: "\(#function)",
  353. echoMetadata: true
  354. )
  355. try await writer.write(message)
  356. }
  357. try await control.clientStream(request: request) { response in
  358. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  359. XCTAssertEqual(error.code, .aborted, "\(pair)")
  360. XCTAssertEqual(error.message, "\(#function)", "\(pair)")
  361. let trailing = error.metadata
  362. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  363. }
  364. // No initial metadata for trailers-only.
  365. XCTAssertEqual(response.metadata, [:])
  366. let trailing = response.trailingMetadata
  367. XCTAssertEqual(Array(trailing["echo-test-key"]), ["test-value"], "\(pair)")
  368. }
  369. }
  370. }
  371. func testServerStreamingOK() async throws {
  372. try await self.forEachTransportPair { control, _, pair in
  373. let metadata: Metadata = ["test-key": "test-value"]
  374. let input = ControlInput.with {
  375. $0.echoMetadataInHeaders = true
  376. $0.echoMetadataInTrailers = true
  377. $0.numberOfMessages = 5
  378. $0.payloadParameters = .with {
  379. $0.content = 42
  380. $0.size = 1024
  381. }
  382. }
  383. let request = ClientRequest(message: input, metadata: metadata)
  384. try await control.serverStream(request: request) { response in
  385. switch response.accepted {
  386. case .success(let contents):
  387. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  388. var messagesReceived = 0
  389. for try await part in contents.bodyParts {
  390. switch part {
  391. case .message(let message):
  392. messagesReceived += 1
  393. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024))
  394. case .trailingMetadata(let metadata):
  395. XCTAssertEqual(Array(metadata["echo-test-key"]), ["test-value"], "\(pair)")
  396. }
  397. }
  398. XCTAssertEqual(messagesReceived, 5)
  399. case .failure(let error):
  400. throw error
  401. }
  402. }
  403. }
  404. }
  405. func testServerStreamingEmptyOK() async throws {
  406. try await self.forEachTransportPair { control, _, pair in
  407. let metadata: Metadata = ["test-key": "test-value"]
  408. // Echo back metadata, but don't send any messages.
  409. let input = ControlInput.with {
  410. $0.echoMetadataInHeaders = true
  411. $0.echoMetadataInTrailers = true
  412. }
  413. let request = ClientRequest(message: input, metadata: metadata)
  414. try await control.serverStream(request: request) { response in
  415. switch response.accepted {
  416. case .success(let contents):
  417. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  418. for try await part in contents.bodyParts {
  419. switch part {
  420. case .message:
  421. XCTFail("Unexpected message")
  422. case .trailingMetadata(let metadata):
  423. XCTAssertEqual(Array(metadata["echo-test-key"]), ["test-value"], "\(pair)")
  424. }
  425. }
  426. case .failure(let error):
  427. throw error
  428. }
  429. }
  430. }
  431. }
  432. func testServerStreamingNotOK() async throws {
  433. try await self.forEachTransportPair { control, _, pair in
  434. let metadata: Metadata = ["test-key": "test-value"]
  435. let input = ControlInput.with {
  436. $0.echoMetadataInHeaders = true
  437. $0.echoMetadataInTrailers = true
  438. $0.numberOfMessages = 5
  439. $0.payloadParameters = .with {
  440. $0.content = 42
  441. $0.size = 1024
  442. }
  443. $0.status = .with {
  444. $0.code = .aborted
  445. $0.message = "\(#function)"
  446. }
  447. }
  448. let request = ClientRequest(message: input, metadata: metadata)
  449. try await control.serverStream(request: request) { response in
  450. switch response.accepted {
  451. case .success(let contents):
  452. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  453. var messagesReceived = 0
  454. do {
  455. for try await part in contents.bodyParts {
  456. switch part {
  457. case .message(let message):
  458. messagesReceived += 1
  459. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024))
  460. case .trailingMetadata:
  461. XCTFail("Unexpected trailing metadata, should be provided in RPCError")
  462. }
  463. }
  464. XCTFail("Expected error to be thrown")
  465. } catch let error as RPCError {
  466. XCTAssertEqual(error.code, .aborted)
  467. XCTAssertEqual(error.message, "\(#function)")
  468. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  469. }
  470. XCTAssertEqual(messagesReceived, 5)
  471. case .failure(let error):
  472. throw error
  473. }
  474. }
  475. }
  476. }
  477. func testServerStreamingEmptyNotOK() async throws {
  478. try await self.forEachTransportPair { control, _, pair in
  479. let metadata: Metadata = ["test-key": "test-value"]
  480. let input = ControlInput.with {
  481. $0.echoMetadataInHeaders = true
  482. $0.echoMetadataInTrailers = true
  483. $0.status = .with {
  484. $0.code = .aborted
  485. $0.message = "\(#function)"
  486. }
  487. }
  488. let request = ClientRequest(message: input, metadata: metadata)
  489. try await control.serverStream(request: request) { response in
  490. switch response.accepted {
  491. case .success(let contents):
  492. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  493. do {
  494. for try await _ in contents.bodyParts {
  495. XCTFail("Unexpected message, \(pair)")
  496. }
  497. XCTFail("Expected error to be thrown")
  498. } catch let error as RPCError {
  499. XCTAssertEqual(error.code, .aborted)
  500. XCTAssertEqual(error.message, "\(#function)")
  501. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  502. }
  503. case .failure(let error):
  504. throw error
  505. }
  506. }
  507. }
  508. }
  509. func testServerStreamingRejected() async throws {
  510. try await self.forEachTransportPair { control, _, pair in
  511. let metadata: Metadata = ["test-key": "test-value"]
  512. let request = ClientRequest<ControlInput>(
  513. message: .trailersOnly(code: .aborted, message: "\(#function)", echoMetadata: true),
  514. metadata: metadata
  515. )
  516. try await control.serverStream(request: request) { response in
  517. switch response.accepted {
  518. case .success:
  519. XCTFail("Expected RPC to be rejected \(pair)")
  520. case .failure(let error):
  521. XCTAssertEqual(error.code, .aborted, "\(pair)")
  522. XCTAssertEqual(error.message, "\(#function)", "\(pair)")
  523. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  524. }
  525. }
  526. }
  527. }
  528. func testBidiStreamingOK() async throws {
  529. try await self.forEachTransportPair { control, _, pair in
  530. let metadata: Metadata = ["test-key": "test-value"]
  531. let request = StreamingClientRequest(
  532. of: ControlInput.self,
  533. metadata: metadata
  534. ) { writer in
  535. try await writer.write(.echoMetadata)
  536. // Send a few messages, each is echo'd back.
  537. try await writer.write(.messages(1, repeating: 42, count: 1024))
  538. try await writer.write(.messages(1, repeating: 42, count: 1024))
  539. try await writer.write(.messages(1, repeating: 42, count: 1024))
  540. // Send the final status.
  541. try await writer.write(.status(code: .ok, message: "", echoMetadata: true))
  542. }
  543. try await control.bidiStream(request: request) { response in
  544. switch response.accepted {
  545. case .success(let contents):
  546. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  547. var messagesReceived = 0
  548. for try await part in contents.bodyParts {
  549. switch part {
  550. case .message(let message):
  551. messagesReceived += 1
  552. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024))
  553. case .trailingMetadata(let metadata):
  554. XCTAssertEqual(Array(metadata["echo-test-key"]), ["test-value"], "\(pair)")
  555. }
  556. }
  557. XCTAssertEqual(messagesReceived, 3)
  558. case .failure(let error):
  559. throw error
  560. }
  561. }
  562. }
  563. }
  564. func testBidiStreamingEmptyOK() async throws {
  565. try await self.forEachTransportPair { control, _, pair in
  566. let request = StreamingClientRequest(of: ControlInput.self) { _ in }
  567. try await control.bidiStream(request: request) { response in
  568. switch response.accepted {
  569. case .success(let contents):
  570. var receivedTrailingMetadata = false
  571. for try await part in contents.bodyParts {
  572. switch part {
  573. case .message:
  574. XCTFail("Unexpected message \(pair)")
  575. case .trailingMetadata:
  576. XCTAssertFalse(receivedTrailingMetadata, "\(pair)")
  577. receivedTrailingMetadata = true
  578. }
  579. }
  580. case .failure(let error):
  581. throw error
  582. }
  583. }
  584. }
  585. }
  586. func testBidiStreamingNotOK() async throws {
  587. try await self.forEachTransportPair { control, _, pair in
  588. let metadata: Metadata = ["test-key": "test-value"]
  589. let request = StreamingClientRequest(
  590. of: ControlInput.self,
  591. metadata: metadata
  592. ) { writer in
  593. try await writer.write(.echoMetadata)
  594. // Send a few messages, each is echo'd back.
  595. try await writer.write(.messages(1, repeating: 42, count: 1024))
  596. try await writer.write(.messages(1, repeating: 42, count: 1024))
  597. try await writer.write(.messages(1, repeating: 42, count: 1024))
  598. // Send the final status.
  599. try await writer.write(.status(code: .aborted, message: "\(#function)", echoMetadata: true))
  600. }
  601. try await control.bidiStream(request: request) { response in
  602. switch response.accepted {
  603. case .success(let contents):
  604. XCTAssertEqual(Array(contents.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  605. var messagesReceived = 0
  606. do {
  607. for try await part in contents.bodyParts {
  608. switch part {
  609. case .message(let message):
  610. messagesReceived += 1
  611. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024))
  612. case .trailingMetadata:
  613. XCTFail("Trailing metadata should be provided by error")
  614. }
  615. }
  616. XCTFail("Should've thrown error \(pair)")
  617. } catch let error as RPCError {
  618. XCTAssertEqual(error.code, .aborted)
  619. XCTAssertEqual(error.message, "\(#function)")
  620. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"], "\(pair)")
  621. }
  622. XCTAssertEqual(messagesReceived, 3)
  623. case .failure(let error):
  624. throw error
  625. }
  626. }
  627. }
  628. }
  629. func testBidiStreamingRejected() async throws {
  630. try await self.forEachTransportPair { control, _, pair in
  631. let metadata: Metadata = ["test-key": "test-value"]
  632. let request = StreamingClientRequest(
  633. of: ControlInput.self,
  634. metadata: metadata
  635. ) { writer in
  636. try await writer.write(
  637. .trailersOnly(
  638. code: .aborted,
  639. message: "\(#function)",
  640. echoMetadata: true
  641. )
  642. )
  643. }
  644. try await control.bidiStream(request: request) { response in
  645. switch response.accepted {
  646. case .success:
  647. XCTFail("Expected RPC to fail \(pair)")
  648. case .failure(let error):
  649. XCTAssertEqual(error.code, .aborted)
  650. XCTAssertEqual(error.message, "\(#function)")
  651. XCTAssertEqual(Array(error.metadata["echo-test-key"]), ["test-value"])
  652. }
  653. }
  654. }
  655. }
  656. // MARK: - Not Implemented
  657. func testUnaryNotImplemented() async throws {
  658. try await self.forEachTransportPair(enableControlService: false) { control, _, pair in
  659. let request = ClientRequest(message: ControlInput())
  660. try await control.unary(request: request) { response in
  661. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  662. XCTAssertEqual(error.code, .unimplemented)
  663. }
  664. }
  665. }
  666. }
  667. func testClientStreamingNotImplemented() async throws {
  668. try await self.forEachTransportPair(enableControlService: false) { control, _, pair in
  669. let request = StreamingClientRequest(of: ControlInput.self) { _ in }
  670. try await control.clientStream(request: request) { response in
  671. XCTAssertThrowsError(ofType: RPCError.self, try response.message) { error in
  672. XCTAssertEqual(error.code, .unimplemented)
  673. }
  674. }
  675. }
  676. }
  677. func testServerStreamingNotImplemented() async throws {
  678. try await self.forEachTransportPair(enableControlService: false) { control, _, pair in
  679. let request = ClientRequest(message: ControlInput())
  680. try await control.serverStream(request: request) { response in
  681. XCTAssertThrowsError(ofType: RPCError.self, try response.accepted.get()) { error in
  682. XCTAssertEqual(error.code, .unimplemented)
  683. }
  684. }
  685. }
  686. }
  687. func testBidiStreamingNotImplemented() async throws {
  688. try await self.forEachTransportPair(enableControlService: false) { control, _, pair in
  689. let request = StreamingClientRequest(of: ControlInput.self) { _ in }
  690. try await control.bidiStream(request: request) { response in
  691. XCTAssertThrowsError(ofType: RPCError.self, try response.accepted.get()) { error in
  692. XCTAssertEqual(error.code, .unimplemented)
  693. }
  694. }
  695. }
  696. }
  697. // MARK: - Compression tests
  698. private func testUnaryCompression(
  699. client: CompressionAlgorithm,
  700. server: CompressionAlgorithm,
  701. control: ControlClient,
  702. pair: Transport
  703. ) async throws {
  704. let message = ControlInput.with {
  705. $0.echoMetadataInHeaders = true
  706. $0.numberOfMessages = 1
  707. $0.payloadParameters = .with {
  708. $0.content = 42
  709. $0.size = 1024
  710. }
  711. }
  712. var options = CallOptions.defaults
  713. options.compression = client
  714. try await control.unary(
  715. request: ClientRequest(message: message),
  716. options: options
  717. ) { response in
  718. // Check the client algorithm.
  719. switch client {
  720. case .deflate, .gzip:
  721. // "echo-grpc-encoding" is the value of "grpc-encoding" sent from the client to the server.
  722. let encoding = Array(response.metadata["echo-grpc-encoding"])
  723. XCTAssertEqual(encoding, ["\(client.name)"], "\(pair)")
  724. case .none:
  725. ()
  726. default:
  727. XCTFail("Unhandled compression '\(client)'")
  728. }
  729. // Check the server algorithm.
  730. switch server {
  731. case .deflate, .gzip:
  732. let encoding = Array(response.metadata["grpc-encoding"])
  733. XCTAssertEqual(encoding, ["\(server.name)"], "\(pair)")
  734. case .none:
  735. ()
  736. default:
  737. XCTFail("Unhandled compression '\(client)'")
  738. }
  739. let message = try response.message
  740. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  741. }
  742. }
  743. private func testClientStreamingCompression(
  744. client: CompressionAlgorithm,
  745. server: CompressionAlgorithm,
  746. control: ControlClient,
  747. pair: Transport
  748. ) async throws {
  749. let request = StreamingClientRequest(of: ControlInput.self) { writer in
  750. try await writer.write(.echoMetadata)
  751. try await writer.write(.noOp)
  752. try await writer.write(.noOp)
  753. try await writer.write(.messages(1, repeating: 42, count: 1024))
  754. }
  755. var options = CallOptions.defaults
  756. options.compression = client
  757. try await control.clientStream(request: request, options: options) { response in
  758. // Check the client algorithm.
  759. switch client {
  760. case .deflate, .gzip:
  761. // "echo-grpc-encoding" is the value of "grpc-encoding" sent from the client to the server.
  762. let encoding = Array(response.metadata["echo-grpc-encoding"])
  763. XCTAssertEqual(encoding, ["\(client.name)"], "\(pair)")
  764. case .none:
  765. ()
  766. default:
  767. XCTFail("Unhandled compression '\(client)'")
  768. }
  769. // Check the server algorithm.
  770. switch server {
  771. case .deflate, .gzip:
  772. let encoding = Array(response.metadata["grpc-encoding"])
  773. XCTAssertEqual(encoding, ["\(server.name)"], "\(pair)")
  774. case .none:
  775. ()
  776. default:
  777. XCTFail("Unhandled compression '\(client)'")
  778. }
  779. let message = try response.message
  780. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  781. }
  782. }
  783. private func testServerStreamingCompression(
  784. client: CompressionAlgorithm,
  785. server: CompressionAlgorithm,
  786. control: ControlClient,
  787. pair: Transport
  788. ) async throws {
  789. let message = ControlInput.with {
  790. $0.echoMetadataInHeaders = true
  791. $0.numberOfMessages = 5
  792. $0.payloadParameters = .with {
  793. $0.content = 42
  794. $0.size = 1024
  795. }
  796. }
  797. var options = CallOptions.defaults
  798. options.compression = client
  799. try await control.serverStream(
  800. request: ClientRequest(message: message),
  801. options: options
  802. ) { response in
  803. // Check the client algorithm.
  804. switch client {
  805. case .deflate, .gzip:
  806. // "echo-grpc-encoding" is the value of "grpc-encoding" sent from the client to the server.
  807. let encoding = Array(response.metadata["echo-grpc-encoding"])
  808. XCTAssertEqual(encoding, ["\(client.name)"], "\(pair)")
  809. case .none:
  810. ()
  811. default:
  812. XCTFail("Unhandled compression '\(client)'")
  813. }
  814. // Check the server algorithm.
  815. switch server {
  816. case .deflate, .gzip:
  817. let encoding = Array(response.metadata["grpc-encoding"])
  818. XCTAssertEqual(encoding, ["\(server.name)"], "\(pair)")
  819. case .none:
  820. ()
  821. default:
  822. XCTFail("Unhandled compression '\(client)'")
  823. }
  824. for try await message in response.messages {
  825. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  826. }
  827. }
  828. }
  829. private func testBidiStreamingCompression(
  830. client: CompressionAlgorithm,
  831. server: CompressionAlgorithm,
  832. control: ControlClient,
  833. pair: Transport
  834. ) async throws {
  835. let request = StreamingClientRequest(of: ControlInput.self) { writer in
  836. try await writer.write(.echoMetadata)
  837. try await writer.write(.messages(1, repeating: 42, count: 1024))
  838. try await writer.write(.messages(1, repeating: 42, count: 1024))
  839. try await writer.write(.messages(1, repeating: 42, count: 1024))
  840. }
  841. var options = CallOptions.defaults
  842. options.compression = client
  843. try await control.bidiStream(request: request, options: options) { response in
  844. // Check the client algorithm.
  845. switch client {
  846. case .deflate, .gzip:
  847. // "echo-grpc-encoding" is the value of "grpc-encoding" sent from the client to the server.
  848. let encoding = Array(response.metadata["echo-grpc-encoding"])
  849. XCTAssertEqual(encoding, ["\(client.name)"], "\(pair)")
  850. case .none:
  851. ()
  852. default:
  853. XCTFail("Unhandled compression '\(client)'")
  854. }
  855. // Check the server algorithm.
  856. switch server {
  857. case .deflate, .gzip:
  858. let encoding = Array(response.metadata["grpc-encoding"])
  859. XCTAssertEqual(encoding, ["\(server.name)"], "\(pair)")
  860. case .none:
  861. ()
  862. default:
  863. XCTFail("Unhandled compression '\(client)'")
  864. }
  865. for try await message in response.messages {
  866. XCTAssertEqual(message.payload, Data(repeating: 42, count: 1024), "\(pair)")
  867. }
  868. }
  869. }
  870. func testUnaryDeflateCompression() async throws {
  871. try await self.forEachTransportPair(
  872. clientCompression: .deflate,
  873. clientEnabledCompression: .deflate,
  874. serverCompression: .deflate
  875. ) { control, _, pair in
  876. try await self.testUnaryCompression(
  877. client: .deflate,
  878. server: .deflate,
  879. control: control,
  880. pair: pair
  881. )
  882. }
  883. }
  884. func testUnaryGzipCompression() async throws {
  885. try await self.forEachTransportPair(
  886. clientCompression: .gzip,
  887. clientEnabledCompression: .gzip,
  888. serverCompression: .gzip
  889. ) { control, _, pair in
  890. try await self.testUnaryCompression(
  891. client: .gzip,
  892. server: .gzip,
  893. control: control,
  894. pair: pair
  895. )
  896. }
  897. }
  898. func testClientStreamingDeflateCompression() async throws {
  899. try await self.forEachTransportPair(
  900. clientCompression: .deflate,
  901. clientEnabledCompression: .deflate,
  902. serverCompression: .deflate
  903. ) { control, _, pair in
  904. try await self.testClientStreamingCompression(
  905. client: .deflate,
  906. server: .deflate,
  907. control: control,
  908. pair: pair
  909. )
  910. }
  911. }
  912. func testClientStreamingGzipCompression() async throws {
  913. try await self.forEachTransportPair(
  914. clientCompression: .gzip,
  915. clientEnabledCompression: .gzip,
  916. serverCompression: .gzip
  917. ) { control, _, pair in
  918. try await self.testClientStreamingCompression(
  919. client: .gzip,
  920. server: .gzip,
  921. control: control,
  922. pair: pair
  923. )
  924. }
  925. }
  926. func testServerStreamingDeflateCompression() async throws {
  927. try await self.forEachTransportPair(
  928. clientCompression: .deflate,
  929. clientEnabledCompression: .deflate,
  930. serverCompression: .deflate
  931. ) { control, _, pair in
  932. try await self.testServerStreamingCompression(
  933. client: .deflate,
  934. server: .deflate,
  935. control: control,
  936. pair: pair
  937. )
  938. }
  939. }
  940. func testServerStreamingGzipCompression() async throws {
  941. try await self.forEachTransportPair(
  942. clientCompression: .gzip,
  943. clientEnabledCompression: .gzip,
  944. serverCompression: .gzip
  945. ) { control, _, pair in
  946. try await self.testServerStreamingCompression(
  947. client: .gzip,
  948. server: .gzip,
  949. control: control,
  950. pair: pair
  951. )
  952. }
  953. }
  954. func testBidiStreamingDeflateCompression() async throws {
  955. try await self.forEachTransportPair(
  956. clientCompression: .deflate,
  957. clientEnabledCompression: .deflate,
  958. serverCompression: .deflate
  959. ) { control, _, pair in
  960. try await self.testBidiStreamingCompression(
  961. client: .deflate,
  962. server: .deflate,
  963. control: control,
  964. pair: pair
  965. )
  966. }
  967. }
  968. func testBidiStreamingGzipCompression() async throws {
  969. try await self.forEachTransportPair(
  970. clientCompression: .gzip,
  971. clientEnabledCompression: .gzip,
  972. serverCompression: .gzip
  973. ) { control, _, pair in
  974. try await self.testBidiStreamingCompression(
  975. client: .gzip,
  976. server: .gzip,
  977. control: control,
  978. pair: pair
  979. )
  980. }
  981. }
  982. func testUnaryUnsupportedCompression() async throws {
  983. try await self.forEachTransportPair(
  984. clientEnabledCompression: .all,
  985. serverCompression: .gzip
  986. ) { control, _, pair in
  987. let message = ControlInput.with {
  988. $0.numberOfMessages = 1
  989. $0.payloadParameters = .with {
  990. $0.content = 42
  991. $0.size = 1024
  992. }
  993. }
  994. let request = ClientRequest(message: message)
  995. var options = CallOptions.defaults
  996. options.compression = .deflate
  997. try await control.unary(request: request, options: options) { response in
  998. switch response.accepted {
  999. case .success:
  1000. XCTFail("RPC should've been rejected")
  1001. case .failure(let error):
  1002. let acceptEncoding = Array(error.metadata["grpc-accept-encoding"])
  1003. // "identity" may or may not be included, so only test for values which must be present.
  1004. XCTAssertTrue(acceptEncoding.contains("gzip"))
  1005. XCTAssertFalse(acceptEncoding.contains("deflate"))
  1006. }
  1007. }
  1008. }
  1009. }
  1010. func testClientStreamingUnsupportedCompression() async throws {
  1011. try await self.forEachTransportPair(
  1012. clientEnabledCompression: .all,
  1013. serverCompression: .gzip
  1014. ) { control, _, pair in
  1015. let request = StreamingClientRequest(of: ControlInput.self) { writer in
  1016. try await writer.write(.noOp)
  1017. }
  1018. var options = CallOptions.defaults
  1019. options.compression = .deflate
  1020. try await control.clientStream(request: request, options: options) { response in
  1021. switch response.accepted {
  1022. case .success:
  1023. XCTFail("RPC should've been rejected")
  1024. case .failure(let error):
  1025. let acceptEncoding = Array(error.metadata["grpc-accept-encoding"])
  1026. // "identity" may or may not be included, so only test for values which must be present.
  1027. XCTAssertTrue(acceptEncoding.contains("gzip"))
  1028. XCTAssertFalse(acceptEncoding.contains("deflate"))
  1029. }
  1030. }
  1031. }
  1032. }
  1033. func testServerStreamingUnsupportedCompression() async throws {
  1034. try await self.forEachTransportPair(
  1035. clientEnabledCompression: .all,
  1036. serverCompression: .gzip
  1037. ) { control, _, pair in
  1038. let message = ControlInput.with {
  1039. $0.numberOfMessages = 1
  1040. $0.payloadParameters = .with {
  1041. $0.content = 42
  1042. $0.size = 1024
  1043. }
  1044. }
  1045. let request = ClientRequest(message: message)
  1046. var options = CallOptions.defaults
  1047. options.compression = .deflate
  1048. try await control.serverStream(request: request, options: options) { response in
  1049. switch response.accepted {
  1050. case .success:
  1051. XCTFail("RPC should've been rejected")
  1052. case .failure(let error):
  1053. let acceptEncoding = Array(error.metadata["grpc-accept-encoding"])
  1054. // "identity" may or may not be included, so only test for values which must be present.
  1055. XCTAssertTrue(acceptEncoding.contains("gzip"))
  1056. XCTAssertFalse(acceptEncoding.contains("deflate"))
  1057. }
  1058. }
  1059. }
  1060. }
  1061. func testBidiStreamingUnsupportedCompression() async throws {
  1062. try await self.forEachTransportPair(
  1063. clientEnabledCompression: .all,
  1064. serverCompression: .gzip
  1065. ) { control, _, pair in
  1066. let request = StreamingClientRequest(of: ControlInput.self) { writer in
  1067. try await writer.write(.noOp)
  1068. }
  1069. var options = CallOptions.defaults
  1070. options.compression = .deflate
  1071. try await control.bidiStream(request: request, options: options) { response in
  1072. switch response.accepted {
  1073. case .success:
  1074. XCTFail("RPC should've been rejected")
  1075. case .failure(let error):
  1076. let acceptEncoding = Array(error.metadata["grpc-accept-encoding"])
  1077. // "identity" may or may not be included, so only test for values which must be present.
  1078. XCTAssertTrue(acceptEncoding.contains("gzip"))
  1079. XCTAssertFalse(acceptEncoding.contains("deflate"))
  1080. }
  1081. }
  1082. }
  1083. }
  1084. func testUnaryTimeoutPropagatedToServer() async throws {
  1085. try await self.forEachTransportPair { control, _, pair in
  1086. let message = ControlInput.with {
  1087. $0.echoMetadataInHeaders = true
  1088. $0.numberOfMessages = 1
  1089. }
  1090. let request = ClientRequest(message: message)
  1091. var options = CallOptions.defaults
  1092. options.timeout = .seconds(10)
  1093. try await control.unary(request: request, options: options) { response in
  1094. let timeout = Array(response.metadata["echo-grpc-timeout"])
  1095. XCTAssertEqual(timeout.count, 1)
  1096. }
  1097. }
  1098. }
  1099. func testClientStreamingTimeoutPropagatedToServer() async throws {
  1100. try await self.forEachTransportPair { control, _, pair in
  1101. let request = StreamingClientRequest(of: ControlInput.self) { writer in
  1102. let message = ControlInput.with {
  1103. $0.echoMetadataInHeaders = true
  1104. $0.numberOfMessages = 1
  1105. }
  1106. try await writer.write(message)
  1107. }
  1108. var options = CallOptions.defaults
  1109. options.timeout = .seconds(10)
  1110. try await control.clientStream(request: request, options: options) { response in
  1111. let timeout = Array(response.metadata["echo-grpc-timeout"])
  1112. XCTAssertEqual(timeout.count, 1)
  1113. }
  1114. }
  1115. }
  1116. func testServerStreamingTimeoutPropagatedToServer() async throws {
  1117. try await self.forEachTransportPair { control, _, pair in
  1118. let message = ControlInput.with {
  1119. $0.echoMetadataInHeaders = true
  1120. $0.numberOfMessages = 1
  1121. }
  1122. let request = ClientRequest(message: message)
  1123. var options = CallOptions.defaults
  1124. options.timeout = .seconds(10)
  1125. try await control.serverStream(request: request, options: options) { response in
  1126. let timeout = Array(response.metadata["echo-grpc-timeout"])
  1127. XCTAssertEqual(timeout.count, 1)
  1128. }
  1129. }
  1130. }
  1131. func testBidiStreamingTimeoutPropagatedToServer() async throws {
  1132. try await self.forEachTransportPair { control, _, pair in
  1133. let request = StreamingClientRequest(of: ControlInput.self) { writer in
  1134. try await writer.write(.echoMetadata)
  1135. }
  1136. var options = CallOptions.defaults
  1137. options.timeout = .seconds(10)
  1138. try await control.bidiStream(request: request, options: options) { response in
  1139. let timeout = Array(response.metadata["echo-grpc-timeout"])
  1140. XCTAssertEqual(timeout.count, 1)
  1141. }
  1142. }
  1143. }
  1144. private static let httpToStatusCodePairs: [(Int, RPCError.Code)] = [
  1145. // See https://github.com/grpc/grpc/blob/7f664c69b2a636386fbf95c16bc78c559734ce0f/doc/http-grpc-status-mapping.md
  1146. (400, .internalError),
  1147. (401, .unauthenticated),
  1148. (403, .permissionDenied),
  1149. (404, .unimplemented),
  1150. (418, .unknown),
  1151. (429, .unavailable),
  1152. (502, .unavailable),
  1153. (503, .unavailable),
  1154. (504, .unavailable),
  1155. (504, .unavailable),
  1156. ]
  1157. func testUnaryAgainstNonGRPCServer() async throws {
  1158. try await self.forEachClientAndHTTPStatusCodeServer { control, kind in
  1159. for (httpCode, expectedStatus) in Self.httpToStatusCodePairs {
  1160. // Tell the server what to respond with.
  1161. let metadata: Metadata = ["response-status": "\(httpCode)"]
  1162. try await control.unary(
  1163. request: ClientRequest(message: .noOp, metadata: metadata)
  1164. ) { response in
  1165. switch response.accepted {
  1166. case .success:
  1167. XCTFail("RPC should have failed with '\(expectedStatus)'")
  1168. case .failure(let error):
  1169. XCTAssertEqual(error.code, expectedStatus)
  1170. }
  1171. }
  1172. }
  1173. }
  1174. }
  1175. func testClientStreamingAgainstNonGRPCServer() async throws {
  1176. try await self.forEachClientAndHTTPStatusCodeServer { control, kind in
  1177. for (httpCode, expectedStatus) in Self.httpToStatusCodePairs {
  1178. // Tell the server what to respond with.
  1179. let request = StreamingClientRequest(
  1180. of: ControlInput.self,
  1181. metadata: ["response-status": "\(httpCode)"]
  1182. ) { _ in
  1183. }
  1184. try await control.clientStream(request: request) { response in
  1185. switch response.accepted {
  1186. case .success:
  1187. XCTFail("RPC should have failed with '\(expectedStatus)'")
  1188. case .failure(let error):
  1189. XCTAssertEqual(error.code, expectedStatus)
  1190. }
  1191. }
  1192. }
  1193. }
  1194. }
  1195. func testServerStreamingAgainstNonGRPCServer() async throws {
  1196. try await self.forEachClientAndHTTPStatusCodeServer { control, kind in
  1197. for (httpCode, expectedStatus) in Self.httpToStatusCodePairs {
  1198. // Tell the server what to respond with.
  1199. let metadata: Metadata = ["response-status": "\(httpCode)"]
  1200. try await control.serverStream(
  1201. request: ClientRequest(message: .noOp, metadata: metadata)
  1202. ) { response in
  1203. switch response.accepted {
  1204. case .success:
  1205. XCTFail("RPC should have failed with '\(expectedStatus)'")
  1206. case .failure(let error):
  1207. XCTAssertEqual(error.code, expectedStatus)
  1208. }
  1209. }
  1210. }
  1211. }
  1212. }
  1213. func testBidiStreamingAgainstNonGRPCServer() async throws {
  1214. try await self.forEachClientAndHTTPStatusCodeServer { control, kind in
  1215. for (httpCode, expectedStatus) in Self.httpToStatusCodePairs {
  1216. // Tell the server what to respond with.
  1217. let request = StreamingClientRequest(
  1218. of: ControlInput.self,
  1219. metadata: ["response-status": "\(httpCode)"]
  1220. ) { _ in
  1221. }
  1222. try await control.bidiStream(request: request) { response in
  1223. switch response.accepted {
  1224. case .success:
  1225. XCTFail("RPC should have failed with '\(expectedStatus)'")
  1226. case .failure(let error):
  1227. XCTAssertEqual(error.code, expectedStatus)
  1228. }
  1229. }
  1230. }
  1231. }
  1232. }
  1233. func testUnaryScheme() async throws {
  1234. try await self.forEachTransportPair { control, _, pair in
  1235. let input = ControlInput.with {
  1236. $0.echoMetadataInHeaders = true
  1237. $0.numberOfMessages = 1
  1238. }
  1239. let request = ClientRequest(message: input)
  1240. try await control.unary(request: request) { response in
  1241. XCTAssertEqual(Array(response.metadata["echo-scheme"]), ["http"])
  1242. }
  1243. }
  1244. }
  1245. func testServerStreamingScheme() async throws {
  1246. try await self.forEachTransportPair { control, _, pair in
  1247. let input = ControlInput.with {
  1248. $0.echoMetadataInHeaders = true
  1249. $0.numberOfMessages = 1
  1250. }
  1251. let request = ClientRequest(message: input)
  1252. try await control.serverStream(request: request) { response in
  1253. XCTAssertEqual(Array(response.metadata["echo-scheme"]), ["http"])
  1254. }
  1255. }
  1256. }
  1257. func testClientStreamingScheme() async throws {
  1258. try await self.forEachTransportPair { control, _, pair in
  1259. let request = StreamingClientRequest(of: ControlInput.self) { writer in
  1260. let input = ControlInput.with {
  1261. $0.echoMetadataInHeaders = true
  1262. $0.numberOfMessages = 1
  1263. }
  1264. try await writer.write(input)
  1265. }
  1266. try await control.clientStream(request: request) { response in
  1267. XCTAssertEqual(Array(response.metadata["echo-scheme"]), ["http"])
  1268. }
  1269. }
  1270. }
  1271. func testBidiStreamingScheme() async throws {
  1272. try await self.forEachTransportPair { control, _, pair in
  1273. let request = StreamingClientRequest(of: ControlInput.self) { writer in
  1274. let input = ControlInput.with {
  1275. $0.echoMetadataInHeaders = true
  1276. $0.numberOfMessages = 1
  1277. }
  1278. try await writer.write(input)
  1279. }
  1280. try await control.bidiStream(request: request) { response in
  1281. XCTAssertEqual(Array(response.metadata["echo-scheme"]), ["http"])
  1282. }
  1283. }
  1284. }
  1285. func testServerCancellation() async throws {
  1286. for kind in [CancellationKind.awaitCancelled, .withCancellationHandler] {
  1287. try await self.forEachTransportPair { control, server, pair in
  1288. let request = ClientRequest(message: kind)
  1289. try await control.waitForCancellation(request: request) { response in
  1290. // Shutdown the client so that it doesn't attempt to reconnect when the server closes.
  1291. control.client.beginGracefulShutdown()
  1292. // Shutdown the server to cancel the RPC.
  1293. server.beginGracefulShutdown()
  1294. // The RPC should complete without any error or response.
  1295. let responses = try await response.messages.reduce(into: []) { $0.append($1) }
  1296. XCTAssert(responses.isEmpty)
  1297. }
  1298. }
  1299. }
  1300. }
  1301. func testUppercaseClientMetadataKey() async throws {
  1302. try await self.forEachTransportPair { control, _, _ in
  1303. let request = ClientRequest<ControlInput>(
  1304. message: .with {
  1305. $0.echoMetadataInHeaders = true
  1306. $0.numberOfMessages = 1
  1307. },
  1308. metadata: ["UPPERCASE-KEY": "value"]
  1309. )
  1310. try await control.unary(request: request) { response in
  1311. // Keys will be lowercase before being sent over the wire.
  1312. XCTAssertEqual(Array(response.metadata["echo-uppercase-key"]), ["value"])
  1313. }
  1314. }
  1315. }
  1316. func testUppercaseServerMetadataKey() async throws {
  1317. try await self.forEachTransportPair { control, _, _ in
  1318. let request = ClientRequest<ControlInput>(
  1319. message: .with {
  1320. $0.initialMetadataToAdd["UPPERCASE-KEY"] = "initial"
  1321. $0.trailingMetadataToAdd["UPPERCASE-KEY"] = "trailing"
  1322. $0.numberOfMessages = 1
  1323. }
  1324. )
  1325. try await control.unary(request: request) { response in
  1326. XCTAssertEqual(Array(response.metadata["uppercase-key"]), ["initial"])
  1327. XCTAssertEqual(Array(response.trailingMetadata["uppercase-key"]), ["trailing"])
  1328. }
  1329. }
  1330. }
  1331. private func checkAuthority(client: GRPCClient, expected: String) async throws {
  1332. let control = ControlClient(wrapping: client)
  1333. let input = ControlInput.with {
  1334. $0.echoMetadataInHeaders = true
  1335. $0.echoMetadataInTrailers = true
  1336. $0.numberOfMessages = 1
  1337. $0.payloadParameters = .with {
  1338. $0.content = 0
  1339. $0.size = 1024
  1340. }
  1341. }
  1342. try await control.unary(request: ClientRequest(message: input)) { response in
  1343. let initial = response.metadata
  1344. XCTAssertEqual(Array(initial["echo-authority"]), [.string(expected)])
  1345. }
  1346. }
  1347. private func testAuthority(
  1348. serverAddress: SocketAddress,
  1349. authorityOverride override: String? = nil,
  1350. clientTarget: (SocketAddress) -> any ResolvableTarget,
  1351. expectedAuthority: (SocketAddress) -> String
  1352. ) async throws {
  1353. try await withGRPCServer(
  1354. transport: .http2NIOPosix(
  1355. address: serverAddress,
  1356. transportSecurity: .plaintext
  1357. ),
  1358. services: [ControlService()]
  1359. ) { server in
  1360. guard let listeningAddress = try await server.listeningAddress else {
  1361. XCTFail("No listening address")
  1362. return
  1363. }
  1364. let target = clientTarget(listeningAddress)
  1365. try await withGRPCClient(
  1366. transport: .http2NIOPosix(
  1367. target: target,
  1368. transportSecurity: .plaintext,
  1369. config: .defaults {
  1370. $0.http2.authority = override
  1371. }
  1372. )
  1373. ) { client in
  1374. try await self.checkAuthority(client: client, expected: expectedAuthority(listeningAddress))
  1375. }
  1376. }
  1377. }
  1378. func testAuthorityDNS() async throws {
  1379. try await self.testAuthority(serverAddress: .ipv4(host: "127.0.0.1", port: 0)) { address in
  1380. return .dns(host: "localhost", port: address.ipv4!.port)
  1381. } expectedAuthority: { address in
  1382. return "localhost:\(address.ipv4!.port)"
  1383. }
  1384. }
  1385. func testOverrideAuthorityDNS() async throws {
  1386. try await self.testAuthority(
  1387. serverAddress: .ipv4(host: "127.0.0.1", port: 0),
  1388. authorityOverride: "respect-my-authority"
  1389. ) { address in
  1390. return .dns(host: "localhost", port: address.ipv4!.port)
  1391. } expectedAuthority: { _ in
  1392. return "respect-my-authority"
  1393. }
  1394. }
  1395. func testAuthorityIPv4() async throws {
  1396. try await self.testAuthority(serverAddress: .ipv4(host: "127.0.0.1", port: 0)) { address in
  1397. return .ipv4(host: "127.0.0.1", port: address.ipv4!.port)
  1398. } expectedAuthority: { address in
  1399. return "127.0.0.1:\(address.ipv4!.port)"
  1400. }
  1401. }
  1402. func testOverrideAuthorityIPv4() async throws {
  1403. try await self.testAuthority(
  1404. serverAddress: .ipv4(host: "127.0.0.1", port: 0),
  1405. authorityOverride: "respect-my-authority"
  1406. ) { address in
  1407. return .ipv4(host: "127.0.0.1", port: address.ipv4!.port)
  1408. } expectedAuthority: { _ in
  1409. return "respect-my-authority"
  1410. }
  1411. }
  1412. func testAuthorityIPv6() async throws {
  1413. try await self.testAuthority(serverAddress: .ipv6(host: "::1", port: 0)) { address in
  1414. return .ipv6(host: "::1", port: address.ipv6!.port)
  1415. } expectedAuthority: { address in
  1416. return "[::1]:\(address.ipv6!.port)"
  1417. }
  1418. }
  1419. func testOverrideAuthorityIPv6() async throws {
  1420. try await self.testAuthority(
  1421. serverAddress: .ipv6(host: "::1", port: 0),
  1422. authorityOverride: "respect-my-authority"
  1423. ) { address in
  1424. return .ipv6(host: "::1", port: address.ipv6!.port)
  1425. } expectedAuthority: { _ in
  1426. return "respect-my-authority"
  1427. }
  1428. }
  1429. func testAuthorityUDS() async throws {
  1430. let path = "test-authority-uds"
  1431. try await self.testAuthority(serverAddress: .unixDomainSocket(path: path)) { address in
  1432. return .unixDomainSocket(path: path)
  1433. } expectedAuthority: { _ in
  1434. return path
  1435. }
  1436. }
  1437. func testAuthorityLocalUDSOverride() async throws {
  1438. let path = "test-authority-local-uds-override"
  1439. try await self.testAuthority(serverAddress: .unixDomainSocket(path: path)) { address in
  1440. return .unixDomainSocket(path: path, authority: "respect-my-authority")
  1441. } expectedAuthority: { _ in
  1442. return "respect-my-authority"
  1443. }
  1444. }
  1445. func testOverrideAuthorityUDS() async throws {
  1446. let path = "test-override-authority-uds"
  1447. try await self.testAuthority(
  1448. serverAddress: .unixDomainSocket(path: path),
  1449. authorityOverride: "respect-my-authority"
  1450. ) { _ in
  1451. return .unixDomainSocket(path: path, authority: "should-be-ignored")
  1452. } expectedAuthority: { _ in
  1453. return "respect-my-authority"
  1454. }
  1455. }
  1456. func testPeerInfoIPv4() async throws {
  1457. try await self.forEachTransportPair(
  1458. serverAddress: .ipv4(host: "127.0.0.1", port: 0)
  1459. ) { control, _, _ in
  1460. let peerInfo = try await control.peerInfo()
  1461. let matches = peerInfo.matches(of: /ipv4:127.0.0.1:\d+/)
  1462. XCTAssertNotNil(matches)
  1463. }
  1464. }
  1465. func testPeerInfoIPv6() async throws {
  1466. try await self.forEachTransportPair(
  1467. serverAddress: .ipv6(host: "::1", port: 0)
  1468. ) { control, _, _ in
  1469. let peerInfo = try await control.peerInfo()
  1470. let matches = peerInfo.matches(of: /ipv6:[::1]:\d+/)
  1471. XCTAssertNotNil(matches)
  1472. }
  1473. }
  1474. func testPeerInfoUDS() async throws {
  1475. let path = "peer-info-uds"
  1476. try await self.forEachTransportPair(
  1477. serverAddress: .unixDomainSocket(path: path)
  1478. ) { control, _, _ in
  1479. let peerInfo = try await control.peerInfo()
  1480. XCTAssertEqual(peerInfo, "unix:peer-info-uds")
  1481. }
  1482. }
  1483. }
  1484. extension [HTTP2TransportTests.Transport] {
  1485. static let supported = [
  1486. HTTP2TransportTests.Transport(server: .posix, client: .posix),
  1487. HTTP2TransportTests.Transport(server: .niots, client: .niots),
  1488. HTTP2TransportTests.Transport(server: .niots, client: .posix),
  1489. HTTP2TransportTests.Transport(server: .posix, client: .niots),
  1490. ]
  1491. }
  1492. extension ControlInput {
  1493. fileprivate static let echoMetadata = Self.with {
  1494. $0.echoMetadataInHeaders = true
  1495. }
  1496. fileprivate static let noOp = Self()
  1497. fileprivate static func messages(
  1498. _ numberOfMessages: Int,
  1499. repeating: UInt8,
  1500. count: Int
  1501. ) -> Self {
  1502. return Self.with {
  1503. $0.numberOfMessages = numberOfMessages
  1504. $0.payloadParameters = .with {
  1505. $0.content = repeating
  1506. $0.size = count
  1507. }
  1508. }
  1509. }
  1510. fileprivate static func status(
  1511. code: GRPCCore.Status.Code,
  1512. message: String,
  1513. echoMetadata: Bool
  1514. ) -> Self {
  1515. return Self.with {
  1516. $0.echoMetadataInTrailers = echoMetadata
  1517. $0.status = .with {
  1518. $0.code = code
  1519. $0.message = message
  1520. }
  1521. }
  1522. }
  1523. fileprivate static func trailersOnly(
  1524. code: GRPCCore.Status.Code,
  1525. message: String,
  1526. echoMetadata: Bool
  1527. ) -> Self {
  1528. return Self.with {
  1529. $0.echoMetadataInTrailers = echoMetadata
  1530. $0.isTrailersOnly = true
  1531. $0.status = .with {
  1532. $0.code = code
  1533. $0.message = message
  1534. }
  1535. }
  1536. }
  1537. }
  1538. extension CompressionAlgorithm {
  1539. var name: String {
  1540. switch self {
  1541. case .deflate:
  1542. return "deflate"
  1543. case .gzip:
  1544. return "gzip"
  1545. case .none:
  1546. return "identity"
  1547. default:
  1548. return ""
  1549. }
  1550. }
  1551. }