InteroperabilityTestCases.swift 34 KB

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