2
0

InteroperabilityTestCases.swift 22 KB

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