InteroperabilityTestCases.swift 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. /*
  2. * Copyright 2019, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import GRPC
  18. import GRPCInteroperabilityTestModels
  19. import NIOHTTP1
  20. import NIOHPACK
  21. /// This test verifies that implementations support zero-size messages. Ideally, client
  22. /// implementations would verify that the request and response were zero bytes serialized, but
  23. /// this is generally prohibitive to perform, so is not required.
  24. ///
  25. /// Server features:
  26. /// - EmptyCall
  27. ///
  28. /// Procedure:
  29. /// 1. Client calls EmptyCall with the default Empty message
  30. ///
  31. /// Client asserts:
  32. /// - call was successful
  33. /// - response is non-null
  34. class EmptyUnary: InteroperabilityTest {
  35. func run(using connection: ClientConnection) throws {
  36. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  37. let call = client.emptyCall(Grpc_Testing_Empty())
  38. try waitAndAssertEqual(call.response, Grpc_Testing_Empty())
  39. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  40. }
  41. }
  42. /// This test verifies that gRPC requests marked as cacheable use GET verb instead of POST, and
  43. /// that server sets appropriate cache control headers for the response to be cached by a proxy.
  44. /// This test requires that the server is behind a caching proxy. Use of current timestamp in the
  45. /// request prevents accidental cache matches left over from previous tests.
  46. ///
  47. /// Server features:
  48. /// - CacheableUnaryCall
  49. ///
  50. /// Procedure:
  51. /// 1. Client calls CacheableUnaryCall with SimpleRequest request with payload set to current
  52. /// timestamp. Timestamp format is irrelevant, and resolution is in nanoseconds. Client adds a
  53. /// x-user-ip header with value 1.2.3.4 to the request. This is done since some proxys such as
  54. /// GFE will not cache requests from localhost. Client marks the request as cacheable by
  55. /// setting the cacheable flag in the request context. Longer term this should be driven by
  56. /// the method option specified in the proto file itself.
  57. /// 2. Client calls CacheableUnaryCall again immediately with the same request and configuration
  58. /// as the previous call.
  59. ///
  60. /// Client asserts:
  61. /// - Both calls were successful
  62. /// - The payload body of both responses is the same.
  63. class CacheableUnary: InteroperabilityTest {
  64. func run(using connection: ClientConnection) throws {
  65. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  66. var timestamp = DispatchTime.now().rawValue
  67. let request = Grpc_Testing_SimpleRequest.withPayload(of: .bytes(of: &timestamp))
  68. let headers: HPACKHeaders = ["x-user-ip": "1.2.3.4"]
  69. let callOptions = CallOptions(customMetadata: headers, cacheable: true)
  70. let call1 = client.cacheableUnaryCall(request, callOptions: callOptions)
  71. let call2 = client.cacheableUnaryCall(request, callOptions: callOptions)
  72. // The server ignores the request payload so we must not validate against it.
  73. try waitAndAssertEqual(call1.response.map { $0.payload }, call2.response.map { $0.payload })
  74. try waitAndAssertEqual(call1.status.map { $0.code }, .ok)
  75. try waitAndAssertEqual(call2.status.map { $0.code }, .ok)
  76. }
  77. }
  78. /// This test verifies unary calls succeed in sending messages, and touches on flow control (even
  79. /// if compression is enabled on the channel).
  80. ///
  81. /// Server features:
  82. /// - UnaryCall
  83. ///
  84. /// Procedure:
  85. /// 1. Client calls UnaryCall with:
  86. /// ```
  87. /// {
  88. /// response_size: 314159
  89. /// payload:{
  90. /// body: 271828 bytes of zeros
  91. /// }
  92. /// }
  93. /// ```
  94. ///
  95. /// Client asserts:
  96. /// - call was successful
  97. /// - response payload body is 314159 bytes in size
  98. /// - clients are free to assert that the response payload body contents are zero and comparing
  99. /// the entire response message against a golden response
  100. class LargeUnary: InteroperabilityTest {
  101. func run(using connection: ClientConnection) throws {
  102. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  103. let request = Grpc_Testing_SimpleRequest.with { request in
  104. request.responseSize = 314_159
  105. request.payload = .zeros(count: 271_828)
  106. }
  107. let call = client.unaryCall(request)
  108. try waitAndAssertEqual(call.response.map { $0.payload }, .zeros(count: 314_159))
  109. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  110. }
  111. }
  112. /// This test verifies the client can compress unary messages by sending two unary calls, for
  113. /// compressed and uncompressed payloads. It also sends an initial probing request to verify
  114. /// whether the server supports the CompressedRequest feature by checking if the probing call
  115. /// fails with an `INVALID_ARGUMENT` status.
  116. ///
  117. /// Server features:
  118. /// - UnaryCall
  119. /// - CompressedRequest
  120. ///
  121. /// Procedure:
  122. /// 1. Client calls UnaryCall with the feature probe, an *uncompressed* message:
  123. /// ```
  124. /// {
  125. /// expect_compressed:{
  126. /// value: true
  127. /// }
  128. /// response_size: 314159
  129. /// payload:{
  130. /// body: 271828 bytes of zeros
  131. /// }
  132. /// }
  133. /// ```
  134. /// 2. Client calls UnaryCall with the *compressed* message:
  135. /// ```
  136. /// {
  137. /// expect_compressed:{
  138. /// value: true
  139. /// }
  140. /// response_size: 314159
  141. /// payload:{
  142. /// body: 271828 bytes of zeros
  143. /// }
  144. /// }
  145. /// ```
  146. /// 3. Client calls UnaryCall with the *uncompressed* message:
  147. /// ```
  148. /// {
  149. /// expect_compressed:{
  150. /// value: false
  151. /// }
  152. /// response_size: 314159
  153. /// payload:{
  154. /// body: 271828 bytes of zeros
  155. /// }
  156. /// }
  157. /// ```
  158. ///
  159. /// Client asserts:
  160. /// - First call failed with `INVALID_ARGUMENT` status.
  161. /// - Subsequent calls were successful.
  162. /// - Response payload body is 314159 bytes in size.
  163. /// - Clients are free to assert that the response payload body contents are zeros and comparing the
  164. /// entire response message against a golden response.
  165. class ClientCompressedUnary: InteroperabilityTest {
  166. func configure(defaults: ClientConnection.Configuration) -> ClientConnection.Configuration {
  167. var configuration = defaults
  168. configuration.messageEncoding = .init(forRequests: .gzip, acceptableForResponses: CompressionAlgorithm.all)
  169. return configuration
  170. }
  171. func run(using connection: ClientConnection) throws {
  172. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  173. let compressedRequest = Grpc_Testing_SimpleRequest.with { request in
  174. request.expectCompressed = true
  175. request.responseSize = 314_159
  176. request.payload = .zeros(count: 271_828)
  177. }
  178. var uncompressedRequest = compressedRequest
  179. uncompressedRequest.expectCompressed = false
  180. // For unary RPCs we disable compression at the call level.
  181. var options = CallOptions()
  182. options.disableCompression = true
  183. // With compression expected but *disabled*.
  184. let probe = client.unaryCall(compressedRequest, callOptions: options)
  185. try waitAndAssertEqual(probe.status.map { $0.code }, .invalidArgument)
  186. // With compression expected and enabled.
  187. let compressed = client.unaryCall(compressedRequest)
  188. try waitAndAssertEqual(compressed.response.map { $0.payload }, .zeros(count: 314_159))
  189. try waitAndAssertEqual(compressed.status.map { $0.code }, .ok)
  190. // With compression not expected and disabled.
  191. let uncompressed = client.unaryCall(uncompressedRequest, callOptions: options)
  192. try waitAndAssertEqual(uncompressed.response.map { $0.payload }, .zeros(count: 314_159))
  193. try waitAndAssertEqual(uncompressed.status.map { $0.code }, .ok)
  194. }
  195. }
  196. /// This test verifies the server can compress unary messages. It sends two unary
  197. /// requests, expecting the server's response to be compressed or not according to
  198. /// the `response_compressed` boolean.
  199. ///
  200. /// Whether compression was actually performed is determined by the compression bit
  201. /// in the response's message flags. *Note that some languages may not have access
  202. /// to the message flags, in which case the client will be unable to verify that
  203. /// the `response_compressed` boolean is obeyed by the server*.
  204. ///
  205. ///
  206. /// Server features:
  207. /// - UnaryCall
  208. /// - CompressedResponse
  209. ///
  210. /// Procedure:
  211. /// 1. Client calls UnaryCall with `SimpleRequest`:
  212. /// ```
  213. /// {
  214. /// response_compressed:{
  215. /// value: true
  216. /// }
  217. /// response_size: 314159
  218. /// payload:{
  219. /// body: 271828 bytes of zeros
  220. /// }
  221. /// }
  222. /// ```
  223. /// ```
  224. /// {
  225. /// response_compressed:{
  226. /// value: false
  227. /// }
  228. /// response_size: 314159
  229. /// payload:{
  230. /// body: 271828 bytes of zeros
  231. /// }
  232. /// }
  233. /// ```
  234. ///
  235. /// Client asserts:
  236. /// - call was successful
  237. /// - if supported by the implementation, when `response_compressed` is true, the response MUST have
  238. /// the compressed message flag set.
  239. /// - if supported by the implementation, when `response_compressed` is false, the response MUST NOT
  240. /// have the compressed message flag set.
  241. /// - response payload body is 314159 bytes in size in both cases.
  242. /// - clients are free to assert that the response payload body contents are zero and comparing the
  243. /// entire response message against a golden response
  244. class ServerCompressedUnary: InteroperabilityTest {
  245. func configure(defaults: ClientConnection.Configuration) -> ClientConnection.Configuration {
  246. var configuration = defaults
  247. configuration.messageEncoding = .responsesOnly
  248. return configuration
  249. }
  250. func run(using connection: ClientConnection) throws {
  251. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  252. let compressedRequest = Grpc_Testing_SimpleRequest.with { request in
  253. request.responseCompressed = true
  254. request.responseSize = 314_159
  255. request.payload = .zeros(count: 271_828)
  256. }
  257. let compressed = client.unaryCall(compressedRequest)
  258. // We can't verify that the compression bit was set, instead we verify that the encoding header
  259. // was sent by the server. This isn't quite the same since as it can still be set but the
  260. // compression may be not set.
  261. try waitAndAssert(compressed.initialMetadata) { headers in
  262. return headers.first(name: "grpc-encoding") != nil
  263. }
  264. try waitAndAssertEqual(compressed.response.map { $0.payload }, .zeros(count: 314_159))
  265. try waitAndAssertEqual(compressed.status.map { $0.code }, .ok)
  266. var uncompressedRequest = compressedRequest
  267. uncompressedRequest.responseCompressed.value = false
  268. let uncompressed = client.unaryCall(uncompressedRequest)
  269. // We can't check even check for the 'grpc-encoding' header here since it could be set with the
  270. // compression bit on the message not set.
  271. try waitAndAssertEqual(uncompressed.response.map { $0.payload }, .zeros(count: 314_159))
  272. try waitAndAssertEqual(uncompressed.status.map { $0.code }, .ok)
  273. }
  274. }
  275. /// This test verifies that client-only streaming succeeds.
  276. ///
  277. /// Server features:
  278. /// - StreamingInputCall
  279. ///
  280. /// Procedure:
  281. /// 1. Client calls StreamingInputCall
  282. /// 2. Client sends:
  283. /// ```
  284. /// {
  285. /// payload:{
  286. /// body: 27182 bytes of zeros
  287. /// }
  288. /// }
  289. /// ```
  290. /// 3. Client then sends:
  291. /// ```
  292. /// {
  293. /// payload:{
  294. /// body: 8 bytes of zeros
  295. /// }
  296. /// }
  297. /// ```
  298. /// 4. Client then sends:
  299. /// ```
  300. /// {
  301. /// payload:{
  302. /// body: 1828 bytes of zeros
  303. /// }
  304. /// }
  305. /// ```
  306. /// 5. Client then sends:
  307. /// ```
  308. /// {
  309. /// payload:{
  310. /// body: 45904 bytes of zeros
  311. /// }
  312. /// }
  313. /// ```
  314. /// 6. Client half-closes
  315. ///
  316. /// Client asserts:
  317. /// - call was successful
  318. /// - response aggregated_payload_size is 74922
  319. class ClientStreaming: InteroperabilityTest {
  320. func run(using connection: ClientConnection) throws {
  321. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  322. let call = client.streamingInputCall()
  323. let messagesSent = call.newMessageQueue().flatMap {
  324. call.sendMessage(.withPayload(of: .zeros(count: 27_182)))
  325. }.flatMap {
  326. call.sendMessage(.withPayload(of: .zeros(count: 8)))
  327. }.flatMap {
  328. call.sendMessage(.withPayload(of: .zeros(count: 1_828)))
  329. }.flatMap {
  330. call.sendMessage(.withPayload(of: .zeros(count: 45_904)))
  331. }.flatMap {
  332. call.sendEnd()
  333. }
  334. try messagesSent.wait()
  335. try waitAndAssertEqual(call.response.map { $0.aggregatedPayloadSize }, 74_922)
  336. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  337. }
  338. }
  339. /// This test verifies the client can compress requests on per-message basis by performing a
  340. /// two-request streaming call. It also sends an initial probing request to verify whether the
  341. /// server supports the `CompressedRequest` feature by checking if the probing call fails with
  342. /// an `INVALID_ARGUMENT` status.
  343. ///
  344. /// Procedure:
  345. /// 1. Client calls `StreamingInputCall` and sends the following feature-probing
  346. /// *uncompressed* `StreamingInputCallRequest` message
  347. ///
  348. /// ```
  349. /// {
  350. /// expect_compressed:{
  351. /// value: true
  352. /// }
  353. /// payload:{
  354. /// body: 27182 bytes of zeros
  355. /// }
  356. /// }
  357. /// ```
  358. /// If the call does not fail with `INVALID_ARGUMENT`, the test fails.
  359. /// Otherwise, we continue.
  360. ///
  361. /// 2. Client calls `StreamingInputCall` again, sending the *compressed* message
  362. ///
  363. /// ```
  364. /// {
  365. /// expect_compressed:{
  366. /// value: true
  367. /// }
  368. /// payload:{
  369. /// body: 27182 bytes of zeros
  370. /// }
  371. /// }
  372. /// ```
  373. ///
  374. /// 3. And finally, the *uncompressed* message
  375. /// ```
  376. /// {
  377. /// expect_compressed:{
  378. /// value: false
  379. /// }
  380. /// payload:{
  381. /// body: 45904 bytes of zeros
  382. /// }
  383. /// }
  384. /// ```
  385. ///
  386. /// 4. Client half-closes
  387. ///
  388. /// Client asserts:
  389. /// - First call fails with `INVALID_ARGUMENT`.
  390. /// - Next calls succeeds.
  391. /// - Response aggregated payload size is 73086.
  392. class ClientCompressedStreaming: InteroperabilityTest {
  393. func configure(defaults: ClientConnection.Configuration) -> ClientConnection.Configuration {
  394. var configuration = defaults
  395. configuration.messageEncoding = .init(forRequests: .gzip, acceptableForResponses: CompressionAlgorithm.all)
  396. return configuration
  397. }
  398. func run(using connection: ClientConnection) throws {
  399. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  400. // Does the server support this test?
  401. let probe = client.streamingInputCall()
  402. let probeRequest: Grpc_Testing_StreamingInputCallRequest = .with { request in
  403. request.expectCompressed = true
  404. request.payload = .zeros(count: 27_182)
  405. }
  406. probe.sendMessage(probeRequest, disableCompression: true, promise: nil)
  407. probe.sendEnd(promise: nil)
  408. // We *expect* invalid argument here. If not then the server doesn't support this test.
  409. try waitAndAssertEqual(probe.status.map { $0.code }, .invalidArgument)
  410. // Now for the actual test.
  411. // The first message is identical to the probe message, we'll reuse that.
  412. // The second should not be compressed.
  413. let secondMessage: Grpc_Testing_StreamingInputCallRequest = .with { request in
  414. request.expectCompressed = false
  415. request.payload = .zeros(count: 45_904)
  416. }
  417. let streaming = client.streamingInputCall()
  418. streaming.sendMessage(probeRequest, promise: nil)
  419. streaming.sendMessage(secondMessage, disableCompression: true, promise: nil)
  420. streaming.sendEnd(promise: nil)
  421. try waitAndAssertEqual(streaming.response.map { $0.aggregatedPayloadSize }, 73_086)
  422. try waitAndAssertEqual(streaming.status.map { $0.code }, .ok)
  423. }
  424. }
  425. /// This test verifies that server-only streaming succeeds.
  426. ///
  427. /// Server features:
  428. /// - StreamingOutputCall
  429. ///
  430. /// Procedure:
  431. /// 1. Client calls StreamingOutputCall with StreamingOutputCallRequest:
  432. /// ```
  433. /// {
  434. /// response_parameters:{
  435. /// size: 31415
  436. /// }
  437. /// response_parameters:{
  438. /// size: 9
  439. /// }
  440. /// response_parameters:{
  441. /// size: 2653
  442. /// }
  443. /// response_parameters:{
  444. /// size: 58979
  445. /// }
  446. /// }
  447. /// ```
  448. ///
  449. /// Client asserts:
  450. /// - call was successful
  451. /// - exactly four responses
  452. /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
  453. /// - clients are free to assert that the response payload body contents are zero and
  454. /// comparing the entire response messages against golden responses
  455. class ServerStreaming: InteroperabilityTest {
  456. func run(using connection: ClientConnection) throws {
  457. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  458. let responseSizes = [31_415, 9, 2_653, 58_979]
  459. let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
  460. request.responseParameters = responseSizes.map { .size($0) }
  461. }
  462. var payloads: [Grpc_Testing_Payload] = []
  463. let call = client.streamingOutputCall(request) { response in
  464. payloads.append(response.payload)
  465. }
  466. // Wait for the status first to ensure we've finished collecting responses.
  467. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  468. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  469. }
  470. }
  471. /// This test verifies that the server can compress streaming messages and disable compression on
  472. /// individual messages, expecting the server's response to be compressed or not according to the
  473. /// `response_compressed` boolean.
  474. ///
  475. /// Whether compression was actually performed is determined by the compression bit in the
  476. /// response's message flags. *Note that some languages may not have access to the message flags, in
  477. /// which case the client will be unable to verify that the `response_compressed` boolean is obeyed
  478. /// by the server*.
  479. ///
  480. /// Server features:
  481. /// - StreamingOutputCall
  482. /// - CompressedResponse
  483. ///
  484. /// Procedure:
  485. /// 1. Client calls StreamingOutputCall with `StreamingOutputCallRequest`:
  486. /// ```
  487. /// {
  488. /// response_parameters:{
  489. /// compressed: {
  490. /// value: true
  491. /// }
  492. /// size: 31415
  493. /// }
  494. /// response_parameters:{
  495. /// compressed: {
  496. /// value: false
  497. /// }
  498. /// size: 92653
  499. /// }
  500. /// }
  501. /// ```
  502. ///
  503. /// Client asserts:
  504. /// - call was successful
  505. /// - exactly two responses
  506. /// - if supported by the implementation, when `response_compressed` is false, the response's
  507. /// messages MUST NOT have the compressed message flag set.
  508. /// - if supported by the implementation, when `response_compressed` is true, the response's
  509. /// messages MUST have the compressed message flag set.
  510. /// - response payload bodies are sized (in order): 31415, 92653
  511. /// - clients are free to assert that the response payload body contents are zero and comparing the
  512. /// entire response messages against golden responses
  513. class ServerCompressedStreaming: InteroperabilityTest {
  514. func configure(defaults: ClientConnection.Configuration) -> ClientConnection.Configuration {
  515. var configuration = defaults
  516. configuration.messageEncoding = .responsesOnly
  517. return configuration
  518. }
  519. func run(using connection: ClientConnection) throws {
  520. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  521. let request: Grpc_Testing_StreamingOutputCallRequest = .with { request in
  522. request.responseParameters = [
  523. .with {
  524. $0.compressed = true
  525. $0.size = 31_415
  526. },
  527. .with {
  528. $0.compressed = false
  529. $0.size = 92_653
  530. }
  531. ]
  532. }
  533. var payloads: [Grpc_Testing_Payload] = []
  534. let rpc = client.streamingOutputCall(request) { response in
  535. payloads.append(response.payload)
  536. }
  537. // We can't verify that the compression bit was set, instead we verify that the encoding header
  538. // was sent by the server. This isn't quite the same since as it can still be set but the
  539. // compression may be not set.
  540. try waitAndAssert(rpc.initialMetadata) { headers in
  541. return headers.first(name: "grpc-encoding") != nil
  542. }
  543. let responseSizes = [31_415, 92_653]
  544. // Wait for the status first to ensure we've finished collecting responses.
  545. try waitAndAssertEqual(rpc.status.map { $0.code }, .ok)
  546. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  547. }
  548. }
  549. /// This test verifies that full duplex bidi is supported.
  550. ///
  551. /// Server features:
  552. /// - FullDuplexCall
  553. ///
  554. /// Procedure:
  555. /// 1. Client calls FullDuplexCall with:
  556. /// ```
  557. /// {
  558. /// response_parameters:{
  559. /// size: 31415
  560. /// }
  561. /// payload:{
  562. /// body: 27182 bytes of zeros
  563. /// }
  564. /// }
  565. /// ```
  566. /// 2. After getting a reply, it sends:
  567. /// ```
  568. /// {
  569. /// response_parameters:{
  570. /// size: 9
  571. /// }
  572. /// payload:{
  573. /// body: 8 bytes of zeros
  574. /// }
  575. /// }
  576. /// ```
  577. /// 3. After getting a reply, it sends:
  578. /// ```
  579. /// {
  580. /// response_parameters:{
  581. /// size: 2653
  582. /// }
  583. /// payload:{
  584. /// body: 1828 bytes of zeros
  585. /// }
  586. /// }
  587. /// ```
  588. /// 4. After getting a reply, it sends:
  589. /// ```
  590. /// {
  591. /// response_parameters:{
  592. /// size: 58979
  593. /// }
  594. /// payload:{
  595. /// body: 45904 bytes of zeros
  596. /// }
  597. /// }
  598. /// ```
  599. /// 5. After getting a reply, client half-closes
  600. ///
  601. /// Client asserts:
  602. /// - call was successful
  603. /// - exactly four responses
  604. /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
  605. /// - clients are free to assert that the response payload body contents are zero and
  606. /// comparing the entire response messages against golden responses
  607. class PingPong: InteroperabilityTest {
  608. func run(using connection: ClientConnection) throws {
  609. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  610. let requestSizes = [27_182, 8, 1_828, 45_904]
  611. let responseSizes = [31_415, 9, 2_653, 58_979]
  612. let responseReceived = DispatchSemaphore(value: 0)
  613. var payloads: [Grpc_Testing_Payload] = []
  614. let call = client.fullDuplexCall { response in
  615. payloads.append(response.payload)
  616. responseReceived.signal()
  617. }
  618. try zip(requestSizes, responseSizes).map { requestSize, responseSize in
  619. Grpc_Testing_StreamingOutputCallRequest.with { request in
  620. request.payload = .zeros(count: requestSize)
  621. request.responseParameters = [.size(responseSize)]
  622. }
  623. }.forEach { request in
  624. call.sendMessage(request, promise: nil)
  625. try assertEqual(responseReceived.wait(timeout: .now() + .seconds(1)), .success)
  626. }
  627. call.sendEnd(promise: nil)
  628. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  629. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  630. }
  631. }
  632. /// This test verifies that streams support having zero-messages in both directions.
  633. ///
  634. /// Server features:
  635. /// - FullDuplexCall
  636. ///
  637. /// Procedure:
  638. /// 1. Client calls FullDuplexCall and then half-closes
  639. ///
  640. /// Client asserts:
  641. /// - call was successful
  642. /// - exactly zero responses
  643. class EmptyStream: InteroperabilityTest {
  644. func run(using connection: ClientConnection) throws {
  645. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  646. var responses: [Grpc_Testing_StreamingOutputCallResponse] = []
  647. let call = client.fullDuplexCall { response in
  648. responses.append(response)
  649. }
  650. try call.sendEnd().wait()
  651. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  652. try assertEqual(responses, [])
  653. }
  654. }
  655. /// This test verifies that custom metadata in either binary or ascii format can be sent as
  656. /// initial-metadata by the client and as both initial- and trailing-metadata by the server.
  657. ///
  658. /// Server features:
  659. /// - UnaryCall
  660. /// - FullDuplexCall
  661. /// - Echo Metadata
  662. ///
  663. /// Procedure:
  664. /// 1. The client attaches custom metadata with the following keys and values
  665. /// to a UnaryCall with request:
  666. /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
  667. /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
  668. /// ```
  669. /// {
  670. /// response_size: 314159
  671. /// payload:{
  672. /// body: 271828 bytes of zeros
  673. /// }
  674. /// }
  675. /// ```
  676. /// 2. The client attaches custom metadata with the following keys and values
  677. /// to a FullDuplexCall with request:
  678. /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
  679. /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
  680. /// ```
  681. /// {
  682. /// response_parameters:{
  683. /// size: 314159
  684. /// }
  685. /// payload:{
  686. /// body: 271828 bytes of zeros
  687. /// }
  688. /// }
  689. /// ```
  690. /// and then half-closes
  691. ///
  692. /// Client asserts:
  693. /// - call was successful
  694. /// - metadata with key "x-grpc-test-echo-initial" and value "test_initial_metadata_value" is
  695. /// received in the initial metadata for calls in Procedure steps 1 and 2.
  696. /// - metadata with key "x-grpc-test-echo-trailing-bin" and value 0xababab is received in the
  697. /// trailing metadata for calls in Procedure steps 1 and 2.
  698. class CustomMetadata: InteroperabilityTest {
  699. let initialMetadataName = "x-grpc-test-echo-initial"
  700. let initialMetadataValue = "test_initial_metadata_value"
  701. let trailingMetadataName = "x-grpc-test-echo-trailing-bin"
  702. let trailingMetadataValue = Data([0xab, 0xab, 0xab]).base64EncodedString()
  703. func checkMetadata<SpecificClientCall>(call: SpecificClientCall) throws where SpecificClientCall: ClientCall {
  704. let initialName = call.initialMetadata.map { $0[self.initialMetadataName] }
  705. try waitAndAssertEqual(initialName, [self.initialMetadataValue])
  706. let trailingName = call.trailingMetadata.map { $0[self.trailingMetadataName] }
  707. try waitAndAssertEqual(trailingName, [self.trailingMetadataValue])
  708. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  709. }
  710. func run(using connection: ClientConnection) throws {
  711. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  712. let unaryRequest = Grpc_Testing_SimpleRequest.with { request in
  713. request.responseSize = 314_159
  714. request.payload = .zeros(count: 217_828)
  715. }
  716. let customMetadata: HPACKHeaders = [
  717. self.initialMetadataName: self.initialMetadataValue,
  718. self.trailingMetadataName: self.trailingMetadataValue
  719. ]
  720. let callOptions = CallOptions(customMetadata: customMetadata)
  721. let unaryCall = client.unaryCall(unaryRequest, callOptions: callOptions)
  722. try self.checkMetadata(call: unaryCall)
  723. let duplexCall = client.fullDuplexCall(callOptions: callOptions) { _ in }
  724. let duplexRequest = Grpc_Testing_StreamingOutputCallRequest.with { request in
  725. request.responseParameters = [.size(314_159)]
  726. request.payload = .zeros(count: 271_828)
  727. }
  728. let messagesSent = duplexCall.newMessageQueue().flatMap {
  729. duplexCall.sendMessage(duplexRequest)
  730. }.flatMap {
  731. duplexCall.sendEnd()
  732. }
  733. try messagesSent.wait()
  734. try self.checkMetadata(call: duplexCall)
  735. }
  736. }
  737. /// This test verifies unary calls succeed in sending messages, and propagate back status code and
  738. /// message sent along with the messages.
  739. ///
  740. /// Server features:
  741. /// - UnaryCall
  742. /// - FullDuplexCall
  743. /// - Echo Status
  744. ///
  745. /// Procedure:
  746. /// 1. Client calls UnaryCall with:
  747. /// ```
  748. /// {
  749. /// response_status:{
  750. /// code: 2
  751. /// message: "test status message"
  752. /// }
  753. /// }
  754. /// ```
  755. /// 2. Client calls FullDuplexCall with:
  756. /// ```
  757. /// {
  758. /// response_status:{
  759. /// code: 2
  760. /// message: "test status message"
  761. /// }
  762. /// }
  763. /// ```
  764. /// 3. and then half-closes
  765. ///
  766. /// Client asserts:
  767. /// - received status code is the same as the sent code for both Procedure steps 1 and 2
  768. /// - received status message is the same as the sent message for both Procedure steps 1 and 2
  769. class StatusCodeAndMessage: InteroperabilityTest {
  770. let expectedCode = 2
  771. let expectedMessage = "test status message"
  772. func checkStatus<SpecificClientCall>(call: SpecificClientCall) throws where SpecificClientCall: ClientCall {
  773. try waitAndAssertEqual(call.status.map { $0.code.rawValue }, self.expectedCode)
  774. try waitAndAssertEqual(call.status.map { $0.message }, self.expectedMessage)
  775. }
  776. func run(using connection: ClientConnection) throws {
  777. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  778. let echoStatus = Grpc_Testing_EchoStatus(code: Int32(self.expectedCode), message: self.expectedMessage)
  779. let unaryCall = client.unaryCall(.withStatus(of: echoStatus))
  780. try self.checkStatus(call: unaryCall)
  781. var responses: [Grpc_Testing_StreamingOutputCallResponse] = []
  782. let duplexCall = client.fullDuplexCall { response in
  783. responses.append(response)
  784. }
  785. try duplexCall.newMessageQueue().flatMap {
  786. duplexCall.sendMessage(.withStatus(of: echoStatus))
  787. }.wait()
  788. try self.checkStatus(call: duplexCall)
  789. try assertEqual(responses, [])
  790. }
  791. }
  792. /// This test verifies Unicode and whitespace is correctly processed in status message. "\t" is
  793. /// horizontal tab. "\r" is carriage return. "\n" is line feed.
  794. ///
  795. /// Server features:
  796. /// - UnaryCall
  797. /// - Echo Status
  798. ///
  799. /// Procedure:
  800. /// 1. Client calls UnaryCall with:
  801. /// ```
  802. /// {
  803. /// response_status:{
  804. /// code: 2
  805. /// message: "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
  806. /// }
  807. /// }
  808. /// ```
  809. ///
  810. /// Client asserts:
  811. /// - received status code is the same as the sent code for Procedure step 1
  812. /// - received status message is the same as the sent message for Procedure step 1, including all
  813. /// whitespace characters
  814. class SpecialStatusMessage: InteroperabilityTest {
  815. func run(using connection: ClientConnection) throws {
  816. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  817. let code = 2
  818. let message = "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
  819. let call = client.unaryCall(.withStatus(of: .init(code: Int32(code), message: message)))
  820. try waitAndAssertEqual(call.status.map { $0.code.rawValue }, code)
  821. try waitAndAssertEqual(call.status.map { $0.message }, message)
  822. }
  823. }
  824. /// This test verifies that calling an unimplemented RPC method returns the UNIMPLEMENTED status
  825. /// code.
  826. ///
  827. /// Server features: N/A
  828. ///
  829. /// Procedure:
  830. /// 1. Client calls grpc.testing.TestService/UnimplementedCall with an empty request (defined as
  831. /// grpc.testing.Empty):
  832. /// ```
  833. /// {
  834. /// }
  835. /// ```
  836. ///
  837. /// Client asserts:
  838. /// - received status code is 12 (UNIMPLEMENTED)
  839. class UnimplementedMethod: InteroperabilityTest {
  840. func run(using connection: ClientConnection) throws {
  841. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  842. let call = client.unimplementedCall(Grpc_Testing_Empty())
  843. try waitAndAssertEqual(call.status.map { $0.code }, .unimplemented)
  844. }
  845. }
  846. /// This test verifies calling an unimplemented server returns the UNIMPLEMENTED status code.
  847. ///
  848. /// Server features: N/A
  849. ///
  850. /// Procedure:
  851. /// 1. Client calls grpc.testing.UnimplementedService/UnimplementedCall with an empty request
  852. /// (defined as grpc.testing.Empty):
  853. /// ```
  854. /// {
  855. /// }
  856. /// ```
  857. ///
  858. /// Client asserts:
  859. /// - received status code is 12 (UNIMPLEMENTED)
  860. class UnimplementedService: InteroperabilityTest {
  861. func run(using connection: ClientConnection) throws {
  862. let client = Grpc_Testing_UnimplementedServiceServiceClient(connection: connection)
  863. let call = client.unimplementedCall(Grpc_Testing_Empty())
  864. try waitAndAssertEqual(call.status.map { $0.code }, .unimplemented)
  865. }
  866. }
  867. /// This test verifies that a request can be cancelled after metadata has been sent but before
  868. /// payloads are sent.
  869. ///
  870. /// Server features:
  871. /// - StreamingInputCall
  872. ///
  873. /// Procedure:
  874. /// 1. Client starts StreamingInputCall
  875. /// 2. Client immediately cancels request
  876. ///
  877. /// Client asserts:
  878. /// - Call completed with status CANCELLED
  879. class CancelAfterBegin: InteroperabilityTest {
  880. func run(using connection: ClientConnection) throws {
  881. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  882. let call = client.streamingInputCall()
  883. call.cancel(promise: nil)
  884. try waitAndAssertEqual(call.status.map { $0.code }, .cancelled)
  885. }
  886. }
  887. /// This test verifies that a request can be cancelled after receiving a message from the server.
  888. ///
  889. /// Server features:
  890. /// - FullDuplexCall
  891. ///
  892. /// Procedure:
  893. /// 1. Client starts FullDuplexCall with
  894. /// ```
  895. /// {
  896. /// response_parameters:{
  897. /// size: 31415
  898. /// }
  899. /// payload:{
  900. /// body: 27182 bytes of zeros
  901. /// }
  902. /// }
  903. /// ```
  904. /// 2. After receiving a response, client cancels request
  905. ///
  906. /// Client asserts:
  907. /// - Call completed with status CANCELLED
  908. class CancelAfterFirstResponse: InteroperabilityTest {
  909. func run(using connection: ClientConnection) throws {
  910. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  911. let promise = client.connection.eventLoop.makePromise(of: Void.self)
  912. let call = client.fullDuplexCall { _ in
  913. promise.succeed(())
  914. }
  915. promise.futureResult.whenSuccess {
  916. call.cancel(promise: nil)
  917. }
  918. let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
  919. request.responseParameters = [.size(31_415)]
  920. request.payload = .zeros(count: 27_182)
  921. }
  922. call.sendMessage(request, promise: nil)
  923. try waitAndAssertEqual(call.status.map { $0.code }, .cancelled)
  924. }
  925. }
  926. /// This test verifies that an RPC request whose lifetime exceeds its configured timeout value
  927. /// will end with the DeadlineExceeded status.
  928. ///
  929. /// Server features:
  930. /// - FullDuplexCall
  931. ///
  932. /// Procedure:
  933. /// 1. Client calls FullDuplexCall with the following request and sets its timeout to 1ms
  934. /// ```
  935. /// {
  936. /// payload:{
  937. /// body: 27182 bytes of zeros
  938. /// }
  939. /// }
  940. /// ```
  941. /// 2. Client waits
  942. ///
  943. /// Client asserts:
  944. /// - Call completed with status DEADLINE_EXCEEDED.
  945. class TimeoutOnSleepingServer: InteroperabilityTest {
  946. func run(using connection: ClientConnection) throws {
  947. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  948. let callOptions = CallOptions(timeout: try .milliseconds(1))
  949. let call = client.fullDuplexCall(callOptions: callOptions) { _ in }
  950. try waitAndAssertEqual(call.status.map { $0.code }, .deadlineExceeded)
  951. }
  952. }