InteroperabilityTestCases.swift 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  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_TestServiceClient(channel: 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_TestServiceClient(channel: 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_TestServiceClient(channel: 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 run(using connection: ClientConnection) throws {
  167. let client = Grpc_Testing_TestServiceClient(channel: connection)
  168. let compressedRequest = Grpc_Testing_SimpleRequest.with { request in
  169. request.expectCompressed = true
  170. request.responseSize = 314_159
  171. request.payload = .zeros(count: 271_828)
  172. }
  173. var uncompressedRequest = compressedRequest
  174. uncompressedRequest.expectCompressed = false
  175. // For unary RPCs we disable compression at the call level.
  176. // With compression expected but *disabled*.
  177. let probe = client.unaryCall(compressedRequest)
  178. try waitAndAssertEqual(probe.status.map { $0.code }, .invalidArgument)
  179. // With compression expected and enabled.
  180. let options = CallOptions(messageEncoding: .enabled(.init(forRequests: .gzip, decompressionLimit: .absolute(1024 * 1024))))
  181. let compressed = client.unaryCall(compressedRequest, callOptions: options)
  182. try waitAndAssertEqual(compressed.response.map { $0.payload }, .zeros(count: 314_159))
  183. try waitAndAssertEqual(compressed.status.map { $0.code }, .ok)
  184. // With compression not expected and disabled.
  185. let uncompressed = client.unaryCall(uncompressedRequest)
  186. try waitAndAssertEqual(uncompressed.response.map { $0.payload }, .zeros(count: 314_159))
  187. try waitAndAssertEqual(uncompressed.status.map { $0.code }, .ok)
  188. }
  189. }
  190. /// This test verifies the server can compress unary messages. It sends two unary
  191. /// requests, expecting the server's response to be compressed or not according to
  192. /// the `response_compressed` boolean.
  193. ///
  194. /// Whether compression was actually performed is determined by the compression bit
  195. /// in the response's message flags. *Note that some languages may not have access
  196. /// to the message flags, in which case the client will be unable to verify that
  197. /// the `response_compressed` boolean is obeyed by the server*.
  198. ///
  199. ///
  200. /// Server features:
  201. /// - UnaryCall
  202. /// - CompressedResponse
  203. ///
  204. /// Procedure:
  205. /// 1. Client calls UnaryCall with `SimpleRequest`:
  206. /// ```
  207. /// {
  208. /// response_compressed:{
  209. /// value: true
  210. /// }
  211. /// response_size: 314159
  212. /// payload:{
  213. /// body: 271828 bytes of zeros
  214. /// }
  215. /// }
  216. /// ```
  217. /// ```
  218. /// {
  219. /// response_compressed:{
  220. /// value: false
  221. /// }
  222. /// response_size: 314159
  223. /// payload:{
  224. /// body: 271828 bytes of zeros
  225. /// }
  226. /// }
  227. /// ```
  228. ///
  229. /// Client asserts:
  230. /// - call was successful
  231. /// - if supported by the implementation, when `response_compressed` is true, the response MUST have
  232. /// the compressed message flag set.
  233. /// - if supported by the implementation, when `response_compressed` is false, the response MUST NOT
  234. /// have the compressed message flag set.
  235. /// - response payload body is 314159 bytes in size in both cases.
  236. /// - clients are free to assert that the response payload body contents are zero and comparing the
  237. /// entire response message against a golden response
  238. class ServerCompressedUnary: InteroperabilityTest {
  239. func run(using connection: ClientConnection) throws {
  240. let client = Grpc_Testing_TestServiceClient(channel: connection)
  241. let compressedRequest = Grpc_Testing_SimpleRequest.with { request in
  242. request.responseCompressed = true
  243. request.responseSize = 314_159
  244. request.payload = .zeros(count: 271_828)
  245. }
  246. let options = CallOptions(messageEncoding: .enabled(.responsesOnly(decompressionLimit: .absolute(1024 * 1024))))
  247. let compressed = client.unaryCall(compressedRequest, callOptions: options)
  248. // We can't verify that the compression bit was set, instead we verify that the encoding header
  249. // was sent by the server. This isn't quite the same since as it can still be set but the
  250. // compression may be not set.
  251. try waitAndAssert(compressed.initialMetadata) { headers in
  252. return headers.first(name: "grpc-encoding") != nil
  253. }
  254. try waitAndAssertEqual(compressed.response.map { $0.payload }, .zeros(count: 314_159))
  255. try waitAndAssertEqual(compressed.status.map { $0.code }, .ok)
  256. var uncompressedRequest = compressedRequest
  257. uncompressedRequest.responseCompressed.value = false
  258. let uncompressed = client.unaryCall(uncompressedRequest)
  259. // We can't check even check for the 'grpc-encoding' header here since it could be set with the
  260. // compression bit on the message not set.
  261. try waitAndAssertEqual(uncompressed.response.map { $0.payload }, .zeros(count: 314_159))
  262. try waitAndAssertEqual(uncompressed.status.map { $0.code }, .ok)
  263. }
  264. }
  265. /// This test verifies that client-only streaming succeeds.
  266. ///
  267. /// Server features:
  268. /// - StreamingInputCall
  269. ///
  270. /// Procedure:
  271. /// 1. Client calls StreamingInputCall
  272. /// 2. Client sends:
  273. /// ```
  274. /// {
  275. /// payload:{
  276. /// body: 27182 bytes of zeros
  277. /// }
  278. /// }
  279. /// ```
  280. /// 3. Client then sends:
  281. /// ```
  282. /// {
  283. /// payload:{
  284. /// body: 8 bytes of zeros
  285. /// }
  286. /// }
  287. /// ```
  288. /// 4. Client then sends:
  289. /// ```
  290. /// {
  291. /// payload:{
  292. /// body: 1828 bytes of zeros
  293. /// }
  294. /// }
  295. /// ```
  296. /// 5. Client then sends:
  297. /// ```
  298. /// {
  299. /// payload:{
  300. /// body: 45904 bytes of zeros
  301. /// }
  302. /// }
  303. /// ```
  304. /// 6. Client half-closes
  305. ///
  306. /// Client asserts:
  307. /// - call was successful
  308. /// - response aggregated_payload_size is 74922
  309. class ClientStreaming: InteroperabilityTest {
  310. func run(using connection: ClientConnection) throws {
  311. let client = Grpc_Testing_TestServiceClient(channel: connection)
  312. let call = client.streamingInputCall()
  313. let requests = [27_182, 8, 1_828, 45_904].map { zeros in
  314. Grpc_Testing_StreamingInputCallRequest.withPayload(of: .zeros(count: zeros))
  315. }
  316. call.sendMessages(requests, promise: nil)
  317. call.sendEnd(promise: nil)
  318. try waitAndAssertEqual(call.response.map { $0.aggregatedPayloadSize }, 74_922)
  319. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  320. }
  321. }
  322. /// This test verifies the client can compress requests on per-message basis by performing a
  323. /// two-request streaming call. It also sends an initial probing request to verify whether the
  324. /// server supports the `CompressedRequest` feature by checking if the probing call fails with
  325. /// an `INVALID_ARGUMENT` status.
  326. ///
  327. /// Procedure:
  328. /// 1. Client calls `StreamingInputCall` and sends the following feature-probing
  329. /// *uncompressed* `StreamingInputCallRequest` message
  330. ///
  331. /// ```
  332. /// {
  333. /// expect_compressed:{
  334. /// value: true
  335. /// }
  336. /// payload:{
  337. /// body: 27182 bytes of zeros
  338. /// }
  339. /// }
  340. /// ```
  341. /// If the call does not fail with `INVALID_ARGUMENT`, the test fails.
  342. /// Otherwise, we continue.
  343. ///
  344. /// 2. Client calls `StreamingInputCall` again, sending the *compressed* message
  345. ///
  346. /// ```
  347. /// {
  348. /// expect_compressed:{
  349. /// value: true
  350. /// }
  351. /// payload:{
  352. /// body: 27182 bytes of zeros
  353. /// }
  354. /// }
  355. /// ```
  356. ///
  357. /// 3. And finally, the *uncompressed* message
  358. /// ```
  359. /// {
  360. /// expect_compressed:{
  361. /// value: false
  362. /// }
  363. /// payload:{
  364. /// body: 45904 bytes of zeros
  365. /// }
  366. /// }
  367. /// ```
  368. ///
  369. /// 4. Client half-closes
  370. ///
  371. /// Client asserts:
  372. /// - First call fails with `INVALID_ARGUMENT`.
  373. /// - Next calls succeeds.
  374. /// - Response aggregated payload size is 73086.
  375. class ClientCompressedStreaming: InteroperabilityTest {
  376. func run(using connection: ClientConnection) throws {
  377. let client = Grpc_Testing_TestServiceClient(channel: connection)
  378. // Does the server support this test? To find out we need to send an uncompressed probe. However
  379. // we need to disable compression at the RPC level as we don't have access to whether the
  380. // compression byte is set on messages. As such the corresponding code in the service
  381. // implementation checks against the 'grpc-encoding' header as a best guess. Disabling
  382. // compression here will stop that header from being sent.
  383. let probe = client.streamingInputCall()
  384. let probeRequest: Grpc_Testing_StreamingInputCallRequest = .with { request in
  385. request.expectCompressed = true
  386. request.payload = .zeros(count: 27_182)
  387. }
  388. // Compression is disabled at the RPC level.
  389. probe.sendMessage(probeRequest, promise: nil)
  390. probe.sendEnd(promise: nil)
  391. // We *expect* invalid argument here. If not then the server doesn't support this test.
  392. try waitAndAssertEqual(probe.status.map { $0.code }, .invalidArgument)
  393. // Now for the actual test.
  394. // The first message is identical to the probe message, we'll reuse that.
  395. // The second should not be compressed.
  396. let secondMessage: Grpc_Testing_StreamingInputCallRequest = .with { request in
  397. request.expectCompressed = false
  398. request.payload = .zeros(count: 45_904)
  399. }
  400. let options = CallOptions(messageEncoding: .enabled(.init(forRequests: .gzip, decompressionLimit: .ratio(10))))
  401. let streaming = client.streamingInputCall(callOptions: options)
  402. streaming.sendMessage(probeRequest, compression: .enabled, promise: nil)
  403. streaming.sendMessage(secondMessage, compression: .disabled, promise: nil)
  404. streaming.sendEnd(promise: nil)
  405. try waitAndAssertEqual(streaming.response.map { $0.aggregatedPayloadSize }, 73_086)
  406. try waitAndAssertEqual(streaming.status.map { $0.code }, .ok)
  407. }
  408. }
  409. /// This test verifies that server-only streaming succeeds.
  410. ///
  411. /// Server features:
  412. /// - StreamingOutputCall
  413. ///
  414. /// Procedure:
  415. /// 1. Client calls StreamingOutputCall with StreamingOutputCallRequest:
  416. /// ```
  417. /// {
  418. /// response_parameters:{
  419. /// size: 31415
  420. /// }
  421. /// response_parameters:{
  422. /// size: 9
  423. /// }
  424. /// response_parameters:{
  425. /// size: 2653
  426. /// }
  427. /// response_parameters:{
  428. /// size: 58979
  429. /// }
  430. /// }
  431. /// ```
  432. ///
  433. /// Client asserts:
  434. /// - call was successful
  435. /// - exactly four responses
  436. /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
  437. /// - clients are free to assert that the response payload body contents are zero and
  438. /// comparing the entire response messages against golden responses
  439. class ServerStreaming: InteroperabilityTest {
  440. func run(using connection: ClientConnection) throws {
  441. let client = Grpc_Testing_TestServiceClient(channel: connection)
  442. let responseSizes = [31_415, 9, 2_653, 58_979]
  443. let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
  444. request.responseParameters = responseSizes.map { .size($0) }
  445. }
  446. var payloads: [Grpc_Testing_Payload] = []
  447. let call = client.streamingOutputCall(request) { response in
  448. payloads.append(response.payload)
  449. }
  450. // Wait for the status first to ensure we've finished collecting responses.
  451. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  452. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  453. }
  454. }
  455. /// This test verifies that the server can compress streaming messages and disable compression on
  456. /// individual messages, expecting the server's response to be compressed or not according to the
  457. /// `response_compressed` boolean.
  458. ///
  459. /// Whether compression was actually performed is determined by the compression bit in the
  460. /// response's message flags. *Note that some languages may not have access to the message flags, in
  461. /// which case the client will be unable to verify that the `response_compressed` boolean is obeyed
  462. /// by the server*.
  463. ///
  464. /// Server features:
  465. /// - StreamingOutputCall
  466. /// - CompressedResponse
  467. ///
  468. /// Procedure:
  469. /// 1. Client calls StreamingOutputCall with `StreamingOutputCallRequest`:
  470. /// ```
  471. /// {
  472. /// response_parameters:{
  473. /// compressed: {
  474. /// value: true
  475. /// }
  476. /// size: 31415
  477. /// }
  478. /// response_parameters:{
  479. /// compressed: {
  480. /// value: false
  481. /// }
  482. /// size: 92653
  483. /// }
  484. /// }
  485. /// ```
  486. ///
  487. /// Client asserts:
  488. /// - call was successful
  489. /// - exactly two responses
  490. /// - if supported by the implementation, when `response_compressed` is false, the response's
  491. /// messages MUST NOT have the compressed message flag set.
  492. /// - if supported by the implementation, when `response_compressed` is true, the response's
  493. /// messages MUST have the compressed message flag set.
  494. /// - response payload bodies are sized (in order): 31415, 92653
  495. /// - clients are free to assert that the response payload body contents are zero and comparing the
  496. /// entire response messages against golden responses
  497. class ServerCompressedStreaming: InteroperabilityTest {
  498. func run(using connection: ClientConnection) throws {
  499. let client = Grpc_Testing_TestServiceClient(channel: connection)
  500. let request: Grpc_Testing_StreamingOutputCallRequest = .with { request in
  501. request.responseParameters = [
  502. .with {
  503. $0.compressed = true
  504. $0.size = 31_415
  505. },
  506. .with {
  507. $0.compressed = false
  508. $0.size = 92_653
  509. }
  510. ]
  511. }
  512. let options = CallOptions(messageEncoding: .enabled(.responsesOnly(decompressionLimit: .absolute(1024 * 1024))))
  513. var payloads: [Grpc_Testing_Payload] = []
  514. let rpc = client.streamingOutputCall(request, callOptions: options) { response in
  515. payloads.append(response.payload)
  516. }
  517. // We can't verify that the compression bit was set, instead we verify that the encoding header
  518. // was sent by the server. This isn't quite the same since as it can still be set but the
  519. // compression may be not set.
  520. try waitAndAssert(rpc.initialMetadata) { headers in
  521. return headers.first(name: "grpc-encoding") != nil
  522. }
  523. let responseSizes = [31_415, 92_653]
  524. // Wait for the status first to ensure we've finished collecting responses.
  525. try waitAndAssertEqual(rpc.status.map { $0.code }, .ok)
  526. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  527. }
  528. }
  529. /// This test verifies that full duplex bidi is supported.
  530. ///
  531. /// Server features:
  532. /// - FullDuplexCall
  533. ///
  534. /// Procedure:
  535. /// 1. Client calls FullDuplexCall with:
  536. /// ```
  537. /// {
  538. /// response_parameters:{
  539. /// size: 31415
  540. /// }
  541. /// payload:{
  542. /// body: 27182 bytes of zeros
  543. /// }
  544. /// }
  545. /// ```
  546. /// 2. After getting a reply, it sends:
  547. /// ```
  548. /// {
  549. /// response_parameters:{
  550. /// size: 9
  551. /// }
  552. /// payload:{
  553. /// body: 8 bytes of zeros
  554. /// }
  555. /// }
  556. /// ```
  557. /// 3. After getting a reply, it sends:
  558. /// ```
  559. /// {
  560. /// response_parameters:{
  561. /// size: 2653
  562. /// }
  563. /// payload:{
  564. /// body: 1828 bytes of zeros
  565. /// }
  566. /// }
  567. /// ```
  568. /// 4. After getting a reply, it sends:
  569. /// ```
  570. /// {
  571. /// response_parameters:{
  572. /// size: 58979
  573. /// }
  574. /// payload:{
  575. /// body: 45904 bytes of zeros
  576. /// }
  577. /// }
  578. /// ```
  579. /// 5. After getting a reply, client half-closes
  580. ///
  581. /// Client asserts:
  582. /// - call was successful
  583. /// - exactly four responses
  584. /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
  585. /// - clients are free to assert that the response payload body contents are zero and
  586. /// comparing the entire response messages against golden responses
  587. class PingPong: InteroperabilityTest {
  588. func run(using connection: ClientConnection) throws {
  589. let client = Grpc_Testing_TestServiceClient(channel: connection)
  590. let requestSizes = [27_182, 8, 1_828, 45_904]
  591. let responseSizes = [31_415, 9, 2_653, 58_979]
  592. let responseReceived = DispatchSemaphore(value: 0)
  593. var payloads: [Grpc_Testing_Payload] = []
  594. let call = client.fullDuplexCall { response in
  595. payloads.append(response.payload)
  596. responseReceived.signal()
  597. }
  598. try zip(requestSizes, responseSizes).map { requestSize, responseSize in
  599. Grpc_Testing_StreamingOutputCallRequest.with { request in
  600. request.payload = .zeros(count: requestSize)
  601. request.responseParameters = [.size(responseSize)]
  602. }
  603. }.forEach { request in
  604. call.sendMessage(request, promise: nil)
  605. try assertEqual(responseReceived.wait(timeout: .now() + .seconds(1)), .success)
  606. }
  607. call.sendEnd(promise: nil)
  608. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  609. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  610. }
  611. }
  612. /// This test verifies that streams support having zero-messages in both directions.
  613. ///
  614. /// Server features:
  615. /// - FullDuplexCall
  616. ///
  617. /// Procedure:
  618. /// 1. Client calls FullDuplexCall and then half-closes
  619. ///
  620. /// Client asserts:
  621. /// - call was successful
  622. /// - exactly zero responses
  623. class EmptyStream: InteroperabilityTest {
  624. func run(using connection: ClientConnection) throws {
  625. let client = Grpc_Testing_TestServiceClient(channel: connection)
  626. var responses: [Grpc_Testing_StreamingOutputCallResponse] = []
  627. let call = client.fullDuplexCall { response in
  628. responses.append(response)
  629. }
  630. try call.sendEnd().wait()
  631. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  632. try assertEqual(responses, [])
  633. }
  634. }
  635. /// This test verifies that custom metadata in either binary or ascii format can be sent as
  636. /// initial-metadata by the client and as both initial- and trailing-metadata by the server.
  637. ///
  638. /// Server features:
  639. /// - UnaryCall
  640. /// - FullDuplexCall
  641. /// - Echo Metadata
  642. ///
  643. /// Procedure:
  644. /// 1. The client attaches custom metadata with the following keys and values
  645. /// to a UnaryCall with request:
  646. /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
  647. /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
  648. /// ```
  649. /// {
  650. /// response_size: 314159
  651. /// payload:{
  652. /// body: 271828 bytes of zeros
  653. /// }
  654. /// }
  655. /// ```
  656. /// 2. The client attaches custom metadata with the following keys and values
  657. /// to a FullDuplexCall with request:
  658. /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
  659. /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
  660. /// ```
  661. /// {
  662. /// response_parameters:{
  663. /// size: 314159
  664. /// }
  665. /// payload:{
  666. /// body: 271828 bytes of zeros
  667. /// }
  668. /// }
  669. /// ```
  670. /// and then half-closes
  671. ///
  672. /// Client asserts:
  673. /// - call was successful
  674. /// - metadata with key "x-grpc-test-echo-initial" and value "test_initial_metadata_value" is
  675. /// received in the initial metadata for calls in Procedure steps 1 and 2.
  676. /// - metadata with key "x-grpc-test-echo-trailing-bin" and value 0xababab is received in the
  677. /// trailing metadata for calls in Procedure steps 1 and 2.
  678. class CustomMetadata: InteroperabilityTest {
  679. let initialMetadataName = "x-grpc-test-echo-initial"
  680. let initialMetadataValue = "test_initial_metadata_value"
  681. let trailingMetadataName = "x-grpc-test-echo-trailing-bin"
  682. let trailingMetadataValue = Data([0xab, 0xab, 0xab]).base64EncodedString()
  683. func checkMetadata<SpecificClientCall>(call: SpecificClientCall) throws where SpecificClientCall: ClientCall {
  684. let initialName = call.initialMetadata.map { $0[self.initialMetadataName] }
  685. try waitAndAssertEqual(initialName, [self.initialMetadataValue])
  686. let trailingName = call.trailingMetadata.map { $0[self.trailingMetadataName] }
  687. try waitAndAssertEqual(trailingName, [self.trailingMetadataValue])
  688. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  689. }
  690. func run(using connection: ClientConnection) throws {
  691. let client = Grpc_Testing_TestServiceClient(channel: connection)
  692. let unaryRequest = Grpc_Testing_SimpleRequest.with { request in
  693. request.responseSize = 314_159
  694. request.payload = .zeros(count: 217_828)
  695. }
  696. let customMetadata: HPACKHeaders = [
  697. self.initialMetadataName: self.initialMetadataValue,
  698. self.trailingMetadataName: self.trailingMetadataValue
  699. ]
  700. let callOptions = CallOptions(customMetadata: customMetadata)
  701. let unaryCall = client.unaryCall(unaryRequest, callOptions: callOptions)
  702. try self.checkMetadata(call: unaryCall)
  703. let duplexCall = client.fullDuplexCall(callOptions: callOptions) { _ in }
  704. let duplexRequest = Grpc_Testing_StreamingOutputCallRequest.with { request in
  705. request.responseParameters = [.size(314_159)]
  706. request.payload = .zeros(count: 271_828)
  707. }
  708. duplexCall.sendMessage(duplexRequest, promise: nil)
  709. duplexCall.sendEnd(promise: nil)
  710. try self.checkMetadata(call: duplexCall)
  711. }
  712. }
  713. /// This test verifies unary calls succeed in sending messages, and propagate back status code and
  714. /// message sent along with the messages.
  715. ///
  716. /// Server features:
  717. /// - UnaryCall
  718. /// - FullDuplexCall
  719. /// - Echo Status
  720. ///
  721. /// Procedure:
  722. /// 1. Client calls UnaryCall with:
  723. /// ```
  724. /// {
  725. /// response_status:{
  726. /// code: 2
  727. /// message: "test status message"
  728. /// }
  729. /// }
  730. /// ```
  731. /// 2. Client calls FullDuplexCall with:
  732. /// ```
  733. /// {
  734. /// response_status:{
  735. /// code: 2
  736. /// message: "test status message"
  737. /// }
  738. /// }
  739. /// ```
  740. /// 3. and then half-closes
  741. ///
  742. /// Client asserts:
  743. /// - received status code is the same as the sent code for both Procedure steps 1 and 2
  744. /// - received status message is the same as the sent message for both Procedure steps 1 and 2
  745. class StatusCodeAndMessage: InteroperabilityTest {
  746. let expectedCode = 2
  747. let expectedMessage = "test status message"
  748. func checkStatus<SpecificClientCall>(call: SpecificClientCall) throws where SpecificClientCall: ClientCall {
  749. try waitAndAssertEqual(call.status.map { $0.code.rawValue }, self.expectedCode)
  750. try waitAndAssertEqual(call.status.map { $0.message }, self.expectedMessage)
  751. }
  752. func run(using connection: ClientConnection) throws {
  753. let client = Grpc_Testing_TestServiceClient(channel: connection)
  754. let echoStatus = Grpc_Testing_EchoStatus(code: Int32(self.expectedCode), message: self.expectedMessage)
  755. let unaryCall = client.unaryCall(.withStatus(of: echoStatus))
  756. try self.checkStatus(call: unaryCall)
  757. var responses: [Grpc_Testing_StreamingOutputCallResponse] = []
  758. let duplexCall = client.fullDuplexCall { response in
  759. responses.append(response)
  760. }
  761. duplexCall.sendMessage(.withStatus(of: echoStatus), promise: nil)
  762. duplexCall.sendEnd(promise: nil)
  763. try self.checkStatus(call: duplexCall)
  764. try assertEqual(responses, [])
  765. }
  766. }
  767. /// This test verifies Unicode and whitespace is correctly processed in status message. "\t" is
  768. /// horizontal tab. "\r" is carriage return. "\n" is line feed.
  769. ///
  770. /// Server features:
  771. /// - UnaryCall
  772. /// - Echo Status
  773. ///
  774. /// Procedure:
  775. /// 1. Client calls UnaryCall with:
  776. /// ```
  777. /// {
  778. /// response_status:{
  779. /// code: 2
  780. /// message: "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
  781. /// }
  782. /// }
  783. /// ```
  784. ///
  785. /// Client asserts:
  786. /// - received status code is the same as the sent code for Procedure step 1
  787. /// - received status message is the same as the sent message for Procedure step 1, including all
  788. /// whitespace characters
  789. class SpecialStatusMessage: InteroperabilityTest {
  790. func run(using connection: ClientConnection) throws {
  791. let client = Grpc_Testing_TestServiceClient(channel: connection)
  792. let code = 2
  793. let message = "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
  794. let call = client.unaryCall(.withStatus(of: .init(code: Int32(code), message: message)))
  795. try waitAndAssertEqual(call.status.map { $0.code.rawValue }, code)
  796. try waitAndAssertEqual(call.status.map { $0.message }, message)
  797. }
  798. }
  799. /// This test verifies that calling an unimplemented RPC method returns the UNIMPLEMENTED status
  800. /// code.
  801. ///
  802. /// Server features: N/A
  803. ///
  804. /// Procedure:
  805. /// 1. Client calls grpc.testing.TestService/UnimplementedCall with an empty request (defined as
  806. /// grpc.testing.Empty):
  807. /// ```
  808. /// {
  809. /// }
  810. /// ```
  811. ///
  812. /// Client asserts:
  813. /// - received status code is 12 (UNIMPLEMENTED)
  814. class UnimplementedMethod: InteroperabilityTest {
  815. func run(using connection: ClientConnection) throws {
  816. let client = Grpc_Testing_TestServiceClient(channel: connection)
  817. let call = client.unimplementedCall(Grpc_Testing_Empty())
  818. try waitAndAssertEqual(call.status.map { $0.code }, .unimplemented)
  819. }
  820. }
  821. /// This test verifies calling an unimplemented server returns the UNIMPLEMENTED status code.
  822. ///
  823. /// Server features: N/A
  824. ///
  825. /// Procedure:
  826. /// 1. Client calls grpc.testing.UnimplementedService/UnimplementedCall with an empty request
  827. /// (defined as grpc.testing.Empty):
  828. /// ```
  829. /// {
  830. /// }
  831. /// ```
  832. ///
  833. /// Client asserts:
  834. /// - received status code is 12 (UNIMPLEMENTED)
  835. class UnimplementedService: InteroperabilityTest {
  836. func run(using connection: ClientConnection) throws {
  837. let client = Grpc_Testing_UnimplementedServiceClient(channel: connection)
  838. let call = client.unimplementedCall(Grpc_Testing_Empty())
  839. try waitAndAssertEqual(call.status.map { $0.code }, .unimplemented)
  840. }
  841. }
  842. /// This test verifies that a request can be cancelled after metadata has been sent but before
  843. /// payloads are sent.
  844. ///
  845. /// Server features:
  846. /// - StreamingInputCall
  847. ///
  848. /// Procedure:
  849. /// 1. Client starts StreamingInputCall
  850. /// 2. Client immediately cancels request
  851. ///
  852. /// Client asserts:
  853. /// - Call completed with status CANCELLED
  854. class CancelAfterBegin: InteroperabilityTest {
  855. func run(using connection: ClientConnection) throws {
  856. let client = Grpc_Testing_TestServiceClient(channel: connection)
  857. let call = client.streamingInputCall()
  858. call.cancel(promise: nil)
  859. try waitAndAssertEqual(call.status.map { $0.code }, .cancelled)
  860. }
  861. }
  862. /// This test verifies that a request can be cancelled after receiving a message from the server.
  863. ///
  864. /// Server features:
  865. /// - FullDuplexCall
  866. ///
  867. /// Procedure:
  868. /// 1. Client starts FullDuplexCall with
  869. /// ```
  870. /// {
  871. /// response_parameters:{
  872. /// size: 31415
  873. /// }
  874. /// payload:{
  875. /// body: 27182 bytes of zeros
  876. /// }
  877. /// }
  878. /// ```
  879. /// 2. After receiving a response, client cancels request
  880. ///
  881. /// Client asserts:
  882. /// - Call completed with status CANCELLED
  883. class CancelAfterFirstResponse: InteroperabilityTest {
  884. func run(using connection: ClientConnection) throws {
  885. let client = Grpc_Testing_TestServiceClient(channel: connection)
  886. let promise = connection.eventLoop.makePromise(of: Void.self)
  887. let call = client.fullDuplexCall { _ in
  888. promise.succeed(())
  889. }
  890. promise.futureResult.whenSuccess {
  891. call.cancel(promise: nil)
  892. }
  893. let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
  894. request.responseParameters = [.size(31_415)]
  895. request.payload = .zeros(count: 27_182)
  896. }
  897. call.sendMessage(request, promise: nil)
  898. try waitAndAssertEqual(call.status.map { $0.code }, .cancelled)
  899. }
  900. }
  901. /// This test verifies that an RPC request whose lifetime exceeds its configured timeout value
  902. /// will end with the DeadlineExceeded status.
  903. ///
  904. /// Server features:
  905. /// - FullDuplexCall
  906. ///
  907. /// Procedure:
  908. /// 1. Client calls FullDuplexCall with the following request and sets its timeout to 1ms
  909. /// ```
  910. /// {
  911. /// payload:{
  912. /// body: 27182 bytes of zeros
  913. /// }
  914. /// }
  915. /// ```
  916. /// 2. Client waits
  917. ///
  918. /// Client asserts:
  919. /// - Call completed with status DEADLINE_EXCEEDED.
  920. class TimeoutOnSleepingServer: InteroperabilityTest {
  921. func run(using connection: ClientConnection) throws {
  922. let client = Grpc_Testing_TestServiceClient(channel: connection)
  923. let callOptions = CallOptions(timeLimit: .timeout(.milliseconds(1)))
  924. let call = client.fullDuplexCall(callOptions: callOptions) { _ in }
  925. try waitAndAssertEqual(call.status.map { $0.code }, .deadlineExceeded)
  926. }
  927. }