InteroperabilityTestCases.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  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 NIOHTTP1
  19. /// This test verifies that implementations support zero-size messages. Ideally, client
  20. /// implementations would verify that the request and response were zero bytes serialized, but
  21. /// this is generally prohibitive to perform, so is not required.
  22. ///
  23. /// Server features:
  24. /// - EmptyCall
  25. ///
  26. /// Procedure:
  27. /// 1. Client calls EmptyCall with the default Empty message
  28. ///
  29. /// Client asserts:
  30. /// - call was successful
  31. /// - response is non-null
  32. class EmptyUnary: InteroperabilityTest {
  33. func run(using connection: ClientConnection) throws {
  34. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  35. let call = client.emptyCall(Grpc_Testing_Empty())
  36. try waitAndAssertEqual(call.response, Grpc_Testing_Empty())
  37. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  38. }
  39. }
  40. /// This test verifies that gRPC requests marked as cacheable use GET verb instead of POST, and
  41. /// that server sets appropriate cache control headers for the response to be cached by a proxy.
  42. /// This test requires that the server is behind a caching proxy. Use of current timestamp in the
  43. /// request prevents accidental cache matches left over from previous tests.
  44. ///
  45. /// Server features:
  46. /// - CacheableUnaryCall
  47. ///
  48. /// Procedure:
  49. /// 1. Client calls CacheableUnaryCall with SimpleRequest request with payload set to current
  50. /// timestamp. Timestamp format is irrelevant, and resolution is in nanoseconds. Client adds a
  51. /// x-user-ip header with value 1.2.3.4 to the request. This is done since some proxys such as
  52. /// GFE will not cache requests from localhost. Client marks the request as cacheable by
  53. /// setting the cacheable flag in the request context. Longer term this should be driven by
  54. /// the method option specified in the proto file itself.
  55. /// 2. Client calls CacheableUnaryCall again immediately with the same request and configuration
  56. /// as the previous call.
  57. ///
  58. /// Client asserts:
  59. /// - Both calls were successful
  60. /// - The payload body of both responses is the same.
  61. class CacheableUnary: InteroperabilityTest {
  62. func run(using connection: ClientConnection) throws {
  63. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  64. var timestamp = DispatchTime.now().rawValue
  65. let request = Grpc_Testing_SimpleRequest.withPayload(of: .bytes(of: &timestamp))
  66. var headers = HTTPHeaders()
  67. headers.add(name: "x-user-ip", value: "1.2.3.4")
  68. let callOptions = CallOptions(customMetadata: headers, cacheable: true)
  69. let call1 = client.cacheableUnaryCall(request, callOptions: callOptions)
  70. let call2 = client.cacheableUnaryCall(request, callOptions: callOptions)
  71. // The server ignores the request payload so we must not validate against it.
  72. try waitAndAssertEqual(call1.response.map { $0.payload }, call2.response.map { $0.payload })
  73. try waitAndAssertEqual(call1.status.map { $0.code }, .ok)
  74. try waitAndAssertEqual(call2.status.map { $0.code }, .ok)
  75. }
  76. }
  77. /// This test verifies unary calls succeed in sending messages, and touches on flow control (even
  78. /// if compression is enabled on the channel).
  79. ///
  80. /// Server features:
  81. /// - UnaryCall
  82. ///
  83. /// Procedure:
  84. /// 1. Client calls UnaryCall with:
  85. /// ```
  86. /// {
  87. /// response_size: 314159
  88. /// payload:{
  89. /// body: 271828 bytes of zeros
  90. /// }
  91. /// }
  92. /// ```
  93. ///
  94. /// Client asserts:
  95. /// - call was successful
  96. /// - response payload body is 314159 bytes in size
  97. /// - clients are free to assert that the response payload body contents are zero and comparing
  98. /// the entire response message against a golden response
  99. class LargeUnary: InteroperabilityTest {
  100. func run(using connection: ClientConnection) throws {
  101. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  102. let request = Grpc_Testing_SimpleRequest.with { request in
  103. request.responseSize = 314_159
  104. request.payload = .zeros(count: 271_828)
  105. }
  106. let call = client.unaryCall(request)
  107. try waitAndAssertEqual(call.response.map { $0.payload }, .zeros(count: 314_159))
  108. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  109. }
  110. }
  111. /// This test verifies that client-only streaming succeeds.
  112. ///
  113. /// Server features:
  114. /// - StreamingInputCall
  115. ///
  116. /// Procedure:
  117. /// 1. Client calls StreamingInputCall
  118. /// 2. Client sends:
  119. /// ```
  120. /// {
  121. /// payload:{
  122. /// body: 27182 bytes of zeros
  123. /// }
  124. /// }
  125. /// ```
  126. /// 3. Client then sends:
  127. /// ```
  128. /// {
  129. /// payload:{
  130. /// body: 8 bytes of zeros
  131. /// }
  132. /// }
  133. /// ```
  134. /// 4. Client then sends:
  135. /// ```
  136. /// {
  137. /// payload:{
  138. /// body: 1828 bytes of zeros
  139. /// }
  140. /// }
  141. /// ```
  142. /// 5. Client then sends:
  143. /// ```
  144. /// {
  145. /// payload:{
  146. /// body: 45904 bytes of zeros
  147. /// }
  148. /// }
  149. /// ```
  150. /// 6. Client half-closes
  151. ///
  152. /// Client asserts:
  153. /// - call was successful
  154. /// - response aggregated_payload_size is 74922
  155. class ClientStreaming: InteroperabilityTest {
  156. func run(using connection: ClientConnection) throws {
  157. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  158. let call = client.streamingInputCall()
  159. let messagesSent = call.newMessageQueue().flatMap {
  160. call.sendMessage(.withPayload(of: .zeros(count: 27_182)))
  161. }.flatMap {
  162. call.sendMessage(.withPayload(of: .zeros(count: 8)))
  163. }.flatMap {
  164. call.sendMessage(.withPayload(of: .zeros(count: 1_828)))
  165. }.flatMap {
  166. call.sendMessage(.withPayload(of: .zeros(count: 45_904)))
  167. }.flatMap {
  168. call.sendEnd()
  169. }
  170. try messagesSent.wait()
  171. try waitAndAssertEqual(call.response.map { $0.aggregatedPayloadSize }, 74_922)
  172. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  173. }
  174. }
  175. /// This test verifies that server-only streaming succeeds.
  176. ///
  177. /// Server features:
  178. /// - StreamingOutputCall
  179. ///
  180. /// Procedure:
  181. /// 1. Client calls StreamingOutputCall with StreamingOutputCallRequest:
  182. /// ```
  183. /// {
  184. /// response_parameters:{
  185. /// size: 31415
  186. /// }
  187. /// response_parameters:{
  188. /// size: 9
  189. /// }
  190. /// response_parameters:{
  191. /// size: 2653
  192. /// }
  193. /// response_parameters:{
  194. /// size: 58979
  195. /// }
  196. /// }
  197. /// ```
  198. ///
  199. /// Client asserts:
  200. /// - call was successful
  201. /// - exactly four responses
  202. /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
  203. /// - clients are free to assert that the response payload body contents are zero and
  204. /// comparing the entire response messages against golden responses
  205. class ServerStreaming: InteroperabilityTest {
  206. func run(using connection: ClientConnection) throws {
  207. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  208. let responseSizes = [31_415, 9, 2_653, 58_979]
  209. let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
  210. request.responseParameters = responseSizes.map { .size($0) }
  211. }
  212. var payloads: [Grpc_Testing_Payload] = []
  213. let call = client.streamingOutputCall(request) { response in
  214. payloads.append(response.payload)
  215. }
  216. // Wait for the status first to ensure we've finished collecting responses.
  217. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  218. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  219. }
  220. }
  221. /// This test verifies that full duplex bidi is supported.
  222. ///
  223. /// Server features:
  224. /// - FullDuplexCall
  225. ///
  226. /// Procedure:
  227. /// 1. Client calls FullDuplexCall with:
  228. /// ```
  229. /// {
  230. /// response_parameters:{
  231. /// size: 31415
  232. /// }
  233. /// payload:{
  234. /// body: 27182 bytes of zeros
  235. /// }
  236. /// }
  237. /// ```
  238. /// 2. After getting a reply, it sends:
  239. /// ```
  240. /// {
  241. /// response_parameters:{
  242. /// size: 9
  243. /// }
  244. /// payload:{
  245. /// body: 8 bytes of zeros
  246. /// }
  247. /// }
  248. /// ```
  249. /// 3. After getting a reply, it sends:
  250. /// ```
  251. /// {
  252. /// response_parameters:{
  253. /// size: 2653
  254. /// }
  255. /// payload:{
  256. /// body: 1828 bytes of zeros
  257. /// }
  258. /// }
  259. /// ```
  260. /// 4. After getting a reply, it sends:
  261. /// ```
  262. /// {
  263. /// response_parameters:{
  264. /// size: 58979
  265. /// }
  266. /// payload:{
  267. /// body: 45904 bytes of zeros
  268. /// }
  269. /// }
  270. /// ```
  271. /// 5. After getting a reply, client half-closes
  272. ///
  273. /// Client asserts:
  274. /// - call was successful
  275. /// - exactly four responses
  276. /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
  277. /// - clients are free to assert that the response payload body contents are zero and
  278. /// comparing the entire response messages against golden responses
  279. class PingPong: InteroperabilityTest {
  280. func run(using connection: ClientConnection) throws {
  281. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  282. let requestSizes = [27_182, 8, 1_828, 45_904]
  283. let responseSizes = [31_415, 9, 2_653, 58_979]
  284. let responseReceived = DispatchSemaphore(value: 0)
  285. var payloads: [Grpc_Testing_Payload] = []
  286. let call = client.fullDuplexCall { response in
  287. payloads.append(response.payload)
  288. responseReceived.signal()
  289. }
  290. try zip(requestSizes, responseSizes).map { requestSize, responseSize in
  291. Grpc_Testing_StreamingOutputCallRequest.with { request in
  292. request.payload = .zeros(count: requestSize)
  293. request.responseParameters = [.size(responseSize)]
  294. }
  295. }.forEach { request in
  296. call.sendMessage(request, promise: nil)
  297. try assertEqual(responseReceived.wait(timeout: .now() + .seconds(1)), .success)
  298. }
  299. call.sendEnd(promise: nil)
  300. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  301. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  302. }
  303. }
  304. /// This test verifies that streams support having zero-messages in both directions.
  305. ///
  306. /// Server features:
  307. /// - FullDuplexCall
  308. ///
  309. /// Procedure:
  310. /// 1. Client calls FullDuplexCall and then half-closes
  311. ///
  312. /// Client asserts:
  313. /// - call was successful
  314. /// - exactly zero responses
  315. class EmptyStream: InteroperabilityTest {
  316. func run(using connection: ClientConnection) throws {
  317. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  318. var responses: [Grpc_Testing_StreamingOutputCallResponse] = []
  319. let call = client.fullDuplexCall { response in
  320. responses.append(response)
  321. }
  322. try call.sendEnd().wait()
  323. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  324. try assertEqual(responses, [])
  325. }
  326. }
  327. /// This test verifies that custom metadata in either binary or ascii format can be sent as
  328. /// initial-metadata by the client and as both initial- and trailing-metadata by the server.
  329. ///
  330. /// Server features:
  331. /// - UnaryCall
  332. /// - FullDuplexCall
  333. /// - Echo Metadata
  334. ///
  335. /// Procedure:
  336. /// 1. The client attaches custom metadata with the following keys and values
  337. /// to a UnaryCall with request:
  338. /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
  339. /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
  340. /// ```
  341. /// {
  342. /// response_size: 314159
  343. /// payload:{
  344. /// body: 271828 bytes of zeros
  345. /// }
  346. /// }
  347. /// ```
  348. /// 2. The client attaches custom metadata with the following keys and values
  349. /// to a FullDuplexCall with request:
  350. /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
  351. /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
  352. /// ```
  353. /// {
  354. /// response_parameters:{
  355. /// size: 314159
  356. /// }
  357. /// payload:{
  358. /// body: 271828 bytes of zeros
  359. /// }
  360. /// }
  361. /// ```
  362. /// and then half-closes
  363. ///
  364. /// Client asserts:
  365. /// - call was successful
  366. /// - metadata with key "x-grpc-test-echo-initial" and value "test_initial_metadata_value" is
  367. /// received in the initial metadata for calls in Procedure steps 1 and 2.
  368. /// - metadata with key "x-grpc-test-echo-trailing-bin" and value 0xababab is received in the
  369. /// trailing metadata for calls in Procedure steps 1 and 2.
  370. class CustomMetadata: InteroperabilityTest {
  371. let initialMetadataName = "x-grpc-test-echo-initial"
  372. let initialMetadataValue = "test_initial_metadata_value"
  373. let trailingMetadataName = "x-grpc-test-echo-trailing-bin"
  374. let trailingMetadataValue = Data([0xab, 0xab, 0xab]).base64EncodedString()
  375. func checkMetadata<SpecificClientCall>(call: SpecificClientCall) throws where SpecificClientCall: ClientCall {
  376. let initialName = call.initialMetadata.map { $0[self.initialMetadataName] }
  377. try waitAndAssertEqual(initialName, [self.initialMetadataValue])
  378. let trailingName = call.trailingMetadata.map { $0[self.trailingMetadataName] }
  379. try waitAndAssertEqual(trailingName, [self.trailingMetadataValue])
  380. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  381. }
  382. func run(using connection: ClientConnection) throws {
  383. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  384. let unaryRequest = Grpc_Testing_SimpleRequest.with { request in
  385. request.responseSize = 314_159
  386. request.payload = .zeros(count: 217_828)
  387. }
  388. var customMetadata = HTTPHeaders()
  389. customMetadata.add(name: self.initialMetadataName, value: self.initialMetadataValue)
  390. customMetadata.add(name: self.trailingMetadataName, value: self.trailingMetadataValue)
  391. let callOptions = CallOptions(customMetadata: customMetadata)
  392. let unaryCall = client.unaryCall(unaryRequest, callOptions: callOptions)
  393. try self.checkMetadata(call: unaryCall)
  394. let duplexCall = client.fullDuplexCall(callOptions: callOptions) { _ in }
  395. let duplexRequest = Grpc_Testing_StreamingOutputCallRequest.with { request in
  396. request.responseParameters = [.size(314_159)]
  397. request.payload = .zeros(count: 271_828)
  398. }
  399. let messagesSent = duplexCall.newMessageQueue().flatMap {
  400. duplexCall.sendMessage(duplexRequest)
  401. }.flatMap {
  402. duplexCall.sendEnd()
  403. }
  404. try messagesSent.wait()
  405. try self.checkMetadata(call: duplexCall)
  406. }
  407. }
  408. /// This test verifies unary calls succeed in sending messages, and propagate back status code and
  409. /// message sent along with the messages.
  410. ///
  411. /// Server features:
  412. /// - UnaryCall
  413. /// - FullDuplexCall
  414. /// - Echo Status
  415. ///
  416. /// Procedure:
  417. /// 1. Client calls UnaryCall with:
  418. /// ```
  419. /// {
  420. /// response_status:{
  421. /// code: 2
  422. /// message: "test status message"
  423. /// }
  424. /// }
  425. /// ```
  426. /// 2. Client calls FullDuplexCall with:
  427. /// ```
  428. /// {
  429. /// response_status:{
  430. /// code: 2
  431. /// message: "test status message"
  432. /// }
  433. /// }
  434. /// ```
  435. /// 3. and then half-closes
  436. ///
  437. /// Client asserts:
  438. /// - received status code is the same as the sent code for both Procedure steps 1 and 2
  439. /// - received status message is the same as the sent message for both Procedure steps 1 and 2
  440. class StatusCodeAndMessage: InteroperabilityTest {
  441. let expectedCode = 2
  442. let expectedMessage = "test status message"
  443. func checkStatus<SpecificClientCall>(call: SpecificClientCall) throws where SpecificClientCall: ClientCall {
  444. try waitAndAssertEqual(call.status.map { $0.code.rawValue }, self.expectedCode)
  445. try waitAndAssertEqual(call.status.map { $0.message }, self.expectedMessage)
  446. }
  447. func run(using connection: ClientConnection) throws {
  448. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  449. let echoStatus = Grpc_Testing_EchoStatus(code: Int32(self.expectedCode), message: self.expectedMessage)
  450. let unaryCall = client.unaryCall(.withStatus(of: echoStatus))
  451. try self.checkStatus(call: unaryCall)
  452. var responses: [Grpc_Testing_StreamingOutputCallResponse] = []
  453. let duplexCall = client.fullDuplexCall { response in
  454. responses.append(response)
  455. }
  456. try duplexCall.newMessageQueue().flatMap {
  457. duplexCall.sendMessage(.withStatus(of: echoStatus))
  458. }.wait()
  459. try self.checkStatus(call: duplexCall)
  460. try assertEqual(responses, [])
  461. }
  462. }
  463. /// This test verifies Unicode and whitespace is correctly processed in status message. "\t" is
  464. /// horizontal tab. "\r" is carriage return. "\n" is line feed.
  465. ///
  466. /// Server features:
  467. /// - UnaryCall
  468. /// - Echo Status
  469. ///
  470. /// Procedure:
  471. /// 1. Client calls UnaryCall with:
  472. /// ```
  473. /// {
  474. /// response_status:{
  475. /// code: 2
  476. /// message: "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
  477. /// }
  478. /// }
  479. /// ```
  480. ///
  481. /// Client asserts:
  482. /// - received status code is the same as the sent code for Procedure step 1
  483. /// - received status message is the same as the sent message for Procedure step 1, including all
  484. /// whitespace characters
  485. class SpecialStatusMessage: InteroperabilityTest {
  486. func run(using connection: ClientConnection) throws {
  487. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  488. let code = 2
  489. let message = "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
  490. let call = client.unaryCall(.withStatus(of: .init(code: Int32(code), message: message)))
  491. try waitAndAssertEqual(call.status.map { $0.code.rawValue }, code)
  492. try waitAndAssertEqual(call.status.map { $0.message }, message)
  493. }
  494. }
  495. /// This test verifies that calling an unimplemented RPC method returns the UNIMPLEMENTED status
  496. /// code.
  497. ///
  498. /// Server features: N/A
  499. ///
  500. /// Procedure:
  501. /// 1. Client calls grpc.testing.TestService/UnimplementedCall with an empty request (defined as
  502. /// grpc.testing.Empty):
  503. /// ```
  504. /// {
  505. /// }
  506. /// ```
  507. ///
  508. /// Client asserts:
  509. /// - received status code is 12 (UNIMPLEMENTED)
  510. class UnimplementedMethod: InteroperabilityTest {
  511. func run(using connection: ClientConnection) throws {
  512. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  513. let call = client.unimplementedCall(Grpc_Testing_Empty())
  514. try waitAndAssertEqual(call.status.map { $0.code }, .unimplemented)
  515. }
  516. }
  517. /// This test verifies calling an unimplemented server returns the UNIMPLEMENTED status code.
  518. ///
  519. /// Server features: N/A
  520. ///
  521. /// Procedure:
  522. /// 1. Client calls grpc.testing.UnimplementedService/UnimplementedCall with an empty request
  523. /// (defined as grpc.testing.Empty):
  524. /// ```
  525. /// {
  526. /// }
  527. /// ```
  528. ///
  529. /// Client asserts:
  530. /// - received status code is 12 (UNIMPLEMENTED)
  531. class UnimplementedService: InteroperabilityTest {
  532. func run(using connection: ClientConnection) throws {
  533. let client = Grpc_Testing_UnimplementedServiceServiceClient(connection: connection)
  534. let call = client.unimplementedCall(Grpc_Testing_Empty())
  535. try waitAndAssertEqual(call.status.map { $0.code }, .unimplemented)
  536. }
  537. }
  538. /// This test verifies that a request can be cancelled after metadata has been sent but before
  539. /// payloads are sent.
  540. ///
  541. /// Server features:
  542. /// - StreamingInputCall
  543. ///
  544. /// Procedure:
  545. /// 1. Client starts StreamingInputCall
  546. /// 2. Client immediately cancels request
  547. ///
  548. /// Client asserts:
  549. /// - Call completed with status CANCELLED
  550. class CancelAfterBegin: InteroperabilityTest {
  551. func run(using connection: ClientConnection) throws {
  552. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  553. let call = client.streamingInputCall()
  554. call.cancel()
  555. try waitAndAssertEqual(call.status.map { $0.code }, .cancelled)
  556. }
  557. }
  558. /// This test verifies that a request can be cancelled after receiving a message from the server.
  559. ///
  560. /// Server features:
  561. /// - FullDuplexCall
  562. ///
  563. /// Procedure:
  564. /// 1. Client starts FullDuplexCall with
  565. /// ```
  566. /// {
  567. /// response_parameters:{
  568. /// size: 31415
  569. /// }
  570. /// payload:{
  571. /// body: 27182 bytes of zeros
  572. /// }
  573. /// }
  574. /// ```
  575. /// 2. After receiving a response, client cancels request
  576. ///
  577. /// Client asserts:
  578. /// - Call completed with status CANCELLED
  579. class CancelAfterFirstResponse: InteroperabilityTest {
  580. func run(using connection: ClientConnection) throws {
  581. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  582. let promise = client.connection.eventLoop.makePromise(of: Void.self)
  583. let call = client.fullDuplexCall { _ in
  584. promise.succeed(())
  585. }
  586. promise.futureResult.whenSuccess {
  587. call.cancel()
  588. }
  589. let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
  590. request.responseParameters = [.size(31_415)]
  591. request.payload = .zeros(count: 27_182)
  592. }
  593. call.sendMessage(request, promise: nil)
  594. try waitAndAssertEqual(call.status.map { $0.code }, .cancelled)
  595. }
  596. }
  597. /// This test verifies that an RPC request whose lifetime exceeds its configured timeout value
  598. /// will end with the DeadlineExceeded status.
  599. ///
  600. /// Server features:
  601. /// - FullDuplexCall
  602. ///
  603. /// Procedure:
  604. /// 1. Client calls FullDuplexCall with the following request and sets its timeout to 1ms
  605. /// ```
  606. /// {
  607. /// payload:{
  608. /// body: 27182 bytes of zeros
  609. /// }
  610. /// }
  611. /// ```
  612. /// 2. Client waits
  613. ///
  614. /// Client asserts:
  615. /// - Call completed with status DEADLINE_EXCEEDED.
  616. class TimeoutOnSleepingServer: InteroperabilityTest {
  617. func run(using connection: ClientConnection) throws {
  618. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  619. let callOptions = CallOptions(timeout: try .milliseconds(1))
  620. let call = client.fullDuplexCall(callOptions: callOptions) { _ in }
  621. try waitAndAssertEqual(call.status.map { $0.code }, .deadlineExceeded)
  622. }
  623. }