GRPCTests.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. /*
  2. * Copyright 2017, 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 Foundation
  18. @testable import SwiftGRPC
  19. import XCTest
  20. class gRPCTests: XCTestCase {
  21. // We have seen this test flake out in rare cases fairly often due to race conditions.
  22. // To detect such rare errors, we run the tests several times.
  23. // (By now, all known errors should have been fixed, but we'd still like to detect new ones.)
  24. let testRepetitions = 10
  25. func testConnectivity() {
  26. for _ in 0..<testRepetitions {
  27. runTest(useSSL: false)
  28. }
  29. }
  30. func testConnectivitySecure() {
  31. for _ in 0..<testRepetitions {
  32. runTest(useSSL: true)
  33. }
  34. }
  35. static var allTests: [(String, (gRPCTests) -> () throws -> Void)] {
  36. return [
  37. ("testConnectivity", testConnectivity),
  38. ("testConnectivitySecure", testConnectivitySecure)
  39. ]
  40. }
  41. }
  42. let address = "localhost:8085"
  43. let host = "example.com"
  44. let evenClientText = "hello, server!"
  45. let oddClientText = "hello, server, please fail!"
  46. let serverText = "hello, client!"
  47. let initialClientMetadata =
  48. [
  49. "x": "xylophone",
  50. "y": "yu",
  51. "z": "zither"
  52. ]
  53. let initialServerMetadata =
  54. [
  55. "a": "Apple",
  56. "b": "Banana",
  57. "c": "Cherry"
  58. ]
  59. let trailingServerMetadata =
  60. [
  61. // We have more than ten entries here to ensure that even large metadata entries work
  62. // and aren't limited by e.g. a fixed-size entry buffer.
  63. "0": "zero",
  64. "1": "one",
  65. "2": "two",
  66. "3": "three",
  67. "4": "four",
  68. "5": "five",
  69. "6": "six",
  70. "7": "seven",
  71. "8": "eight",
  72. "9": "nine",
  73. "10": "ten",
  74. "11": "eleven",
  75. "12": "twelve"
  76. ]
  77. let steps = 100
  78. let hello = "/hello.unary"
  79. let helloServerStream = "/hello.server-stream"
  80. let helloBiDiStream = "/hello.bidi-stream"
  81. // Return code/message for unary test
  82. let oddStatusMessage = "OK"
  83. let evenStatusMessage = "some other status message"
  84. // Parsing very large messages as String is very inefficient,
  85. // so we avoid it anything above this threshold.
  86. let sizeThresholdForReturningDataVerbatim = 10_000
  87. func runTest(useSSL: Bool) {
  88. gRPC.initialize()
  89. var serverRunningSemaphore: DispatchSemaphore?
  90. // create the server
  91. let server: Server
  92. if useSSL {
  93. server = Server(address: address,
  94. key: String(data: keyForTests, encoding: .utf8)!,
  95. certs: String(data: certificateForTests, encoding: .utf8)!)
  96. } else {
  97. server = Server(address: address)
  98. }
  99. // start the server
  100. do {
  101. serverRunningSemaphore = try runServer(server: server)
  102. } catch {
  103. XCTFail("server error \(error)")
  104. }
  105. // run the client
  106. do {
  107. try runClient(useSSL: useSSL)
  108. } catch {
  109. XCTFail("client error \(error)")
  110. }
  111. // stop the server
  112. server.stop()
  113. // wait until the server has shut down
  114. _ = serverRunningSemaphore!.wait()
  115. }
  116. func verify_metadata(_ metadata: Metadata, expected: [String: String], file: StaticString = #file, line: UInt = #line) {
  117. XCTAssertGreaterThanOrEqual(metadata.count(), expected.count)
  118. var allPresentKeys = Set<String>()
  119. for i in 0..<metadata.count() {
  120. guard let expectedValue = expected[metadata.key(i)!]
  121. else { continue }
  122. allPresentKeys.insert(metadata.key(i)!)
  123. XCTAssertEqual(metadata.value(i), expectedValue, file: file, line: line)
  124. }
  125. XCTAssertEqual(allPresentKeys.sorted(), expected.keys.sorted(), file: file, line: line)
  126. }
  127. func runClient(useSSL: Bool) throws {
  128. let channel: Channel
  129. if useSSL {
  130. channel = Channel(address: address,
  131. certificates: String(data: trustCollectionCertificateForTests, encoding: .utf8)!,
  132. arguments: [.sslTargetNameOverride(host)])
  133. } else {
  134. channel = Channel(address: address, secure: false)
  135. }
  136. channel.host = host
  137. let largeMessage = Data(repeating: 88 /* 'X' */, count: 4_000_000)
  138. for _ in 0..<10 {
  139. // Send several calls to each server we spin up, to ensure that each individual server can handle many requests.
  140. try callUnary(channel: channel)
  141. try callServerStream(channel: channel)
  142. try callBiDiStream(channel: channel)
  143. }
  144. // Test sending a large message.
  145. try callUnaryIndividual(channel: channel, message: largeMessage, shouldSucceed: true)
  146. try callUnaryIndividual(channel: channel, message: largeMessage, shouldSucceed: true)
  147. }
  148. func callUnary(channel: Channel) throws {
  149. let evenMessage = evenClientText.data(using: .utf8)!
  150. let oddMessage = oddClientText.data(using: .utf8)!
  151. for i in 0..<steps {
  152. try callUnaryIndividual(channel: channel,
  153. message: (i % 2) == 0 ? evenMessage : oddMessage,
  154. shouldSucceed: (i % 2) == 0)
  155. }
  156. }
  157. func callUnaryIndividual(channel: Channel, message: Data, shouldSucceed: Bool) throws {
  158. let sem = DispatchSemaphore(value: 0)
  159. let method = hello
  160. let call = channel.makeCall(method)
  161. let metadata = try Metadata(initialClientMetadata)
  162. try call.start(.unary, metadata: metadata, message: message) {
  163. response in
  164. // verify the basic response from the server
  165. XCTAssertEqual(response.statusCode, .ok)
  166. XCTAssertEqual(response.statusMessage, shouldSucceed ? evenStatusMessage : oddStatusMessage)
  167. //print("response.resultData?.count", response.resultData?.count)
  168. // verify the message from the server
  169. if shouldSucceed {
  170. if let resultData = response.resultData {
  171. if resultData.count >= sizeThresholdForReturningDataVerbatim {
  172. XCTAssertEqual(message, resultData)
  173. } else {
  174. let messageString = String(data: resultData, encoding: .utf8)
  175. XCTAssertEqual(messageString, serverText)
  176. }
  177. } else {
  178. XCTFail("callUnary response missing")
  179. }
  180. }
  181. // verify the initial metadata from the server
  182. if let initialMetadata = response.initialMetadata {
  183. verify_metadata(initialMetadata, expected: initialServerMetadata)
  184. } else {
  185. XCTFail("callUnary initial metadata missing")
  186. }
  187. // verify the trailing metadata from the server
  188. if let trailingMetadata = response.trailingMetadata {
  189. verify_metadata(trailingMetadata, expected: trailingServerMetadata)
  190. } else {
  191. XCTFail("callUnary trailing metadata missing")
  192. }
  193. // report completion
  194. sem.signal()
  195. }
  196. // wait for the call to complete
  197. _ = sem.wait()
  198. }
  199. func callServerStream(channel: Channel) throws {
  200. let message = evenClientText.data(using: .utf8)
  201. let metadata = try Metadata(initialClientMetadata)
  202. let sem = DispatchSemaphore(value: 0)
  203. let method = helloServerStream
  204. let call = channel.makeCall(method)
  205. try call.start(.serverStreaming, metadata: metadata, message: message) {
  206. response in
  207. XCTAssertEqual(response.statusCode, .ok)
  208. XCTAssertEqual(response.statusMessage, "Custom Status Message ServerStreaming")
  209. // verify the trailing metadata from the server
  210. if let trailingMetadata = response.trailingMetadata {
  211. verify_metadata(trailingMetadata, expected: trailingServerMetadata)
  212. } else {
  213. XCTFail("callServerStream trailing metadata missing")
  214. }
  215. sem.signal() // signal call is finished
  216. }
  217. for _ in 0..<steps {
  218. let messageSem = DispatchSemaphore(value: 0)
  219. try call.receiveMessage { callResult in
  220. if let data = callResult.resultData {
  221. let messageString = String(data: data, encoding: .utf8)
  222. XCTAssertEqual(messageString, serverText)
  223. } else {
  224. XCTFail("callServerStream unexpected result: \(callResult)")
  225. }
  226. messageSem.signal()
  227. }
  228. _ = messageSem.wait()
  229. }
  230. _ = sem.wait()
  231. }
  232. let clientPing = "ping"
  233. let serverPong = "pong"
  234. func callBiDiStream(channel: Channel) throws {
  235. let metadata = try Metadata(initialClientMetadata)
  236. let sem = DispatchSemaphore(value: 0)
  237. let method = helloBiDiStream
  238. let call = channel.makeCall(method)
  239. try call.start(.bidiStreaming, metadata: metadata, message: nil) {
  240. response in
  241. XCTAssertEqual(response.statusCode, .ok)
  242. XCTAssertEqual(response.statusMessage, "Custom Status Message BiDi")
  243. // verify the trailing metadata from the server
  244. if let trailingMetadata = response.trailingMetadata {
  245. verify_metadata(trailingMetadata, expected: trailingServerMetadata)
  246. } else {
  247. XCTFail("callBiDiStream trailing metadata missing")
  248. }
  249. sem.signal() // signal call is finished
  250. }
  251. // Send pings
  252. let message = clientPing.data(using: .utf8)!
  253. for _ in 0..<steps {
  254. try call.sendMessage(data: message) { err in
  255. XCTAssertNil(err)
  256. }
  257. call.messageQueueEmpty.wait()
  258. }
  259. let closeSem = DispatchSemaphore(value: 0)
  260. try call.close {
  261. closeSem.signal()
  262. }
  263. _ = closeSem.wait()
  264. // Receive pongs
  265. for _ in 0..<steps {
  266. let pongSem = DispatchSemaphore(value: 0)
  267. try call.receiveMessage { callResult in
  268. if let data = callResult.resultData {
  269. let messageString = String(data: data, encoding: .utf8)
  270. XCTAssertEqual(messageString, serverPong)
  271. } else {
  272. XCTFail("callBiDiStream unexpected result: \(callResult)")
  273. }
  274. pongSem.signal()
  275. }
  276. _ = pongSem.wait()
  277. }
  278. _ = sem.wait()
  279. }
  280. func runServer(server: Server) throws -> DispatchSemaphore {
  281. let sem = DispatchSemaphore(value: 0)
  282. server.run { requestHandler in
  283. do {
  284. if let method = requestHandler.method {
  285. switch method {
  286. case hello:
  287. try handleUnary(requestHandler: requestHandler)
  288. case helloServerStream:
  289. try handleServerStream(requestHandler: requestHandler)
  290. case helloBiDiStream:
  291. try handleBiDiStream(requestHandler: requestHandler)
  292. default:
  293. XCTFail("Invalid method \(method)")
  294. }
  295. }
  296. } catch {
  297. XCTFail("error \(error)")
  298. }
  299. }
  300. server.onCompletion = {
  301. // return from runServer()
  302. sem.signal()
  303. }
  304. // wait for the server to exit
  305. return sem
  306. }
  307. func handleUnary(requestHandler: Handler) throws {
  308. XCTAssertEqual(requestHandler.host, host)
  309. XCTAssertEqual(requestHandler.method, hello)
  310. let initialMetadata = requestHandler.requestMetadata
  311. verify_metadata(initialMetadata, expected: initialClientMetadata)
  312. let initialMetadataToSend = try Metadata(initialServerMetadata)
  313. let receiveSem = DispatchSemaphore(value: 0)
  314. var inputMessage: Data?
  315. try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) {
  316. if let messageData = $0 {
  317. inputMessage = messageData
  318. if messageData.count < sizeThresholdForReturningDataVerbatim {
  319. let messageString = String(data: messageData, encoding: .utf8)!
  320. XCTAssertTrue(messageString == evenClientText || messageString == oddClientText,
  321. "handleUnary unexpected message string \(messageString)")
  322. }
  323. } else {
  324. XCTFail("handleUnary message missing")
  325. }
  326. receiveSem.signal()
  327. }
  328. receiveSem.wait()
  329. // We need to return status OK in both cases, as it seems like the server might never send out the last few messages
  330. // once it has been asked to send a non-OK status. Alternatively, we could send a non-OK status here, but then we
  331. // would need to sleep for a few milliseconds before sending the non-OK status.
  332. let replyMessage = (inputMessage == nil || inputMessage!.count < sizeThresholdForReturningDataVerbatim)
  333. ? serverText.data(using: .utf8)!
  334. : inputMessage!
  335. let trailingMetadataToSend = try Metadata(trailingServerMetadata)
  336. if let inputMessage = inputMessage,
  337. inputMessage.count >= sizeThresholdForReturningDataVerbatim
  338. || inputMessage == evenClientText.data(using: .utf8)! {
  339. try requestHandler.sendResponse(message: replyMessage,
  340. status: ServerStatus(code: .ok,
  341. message: evenStatusMessage,
  342. trailingMetadata: trailingMetadataToSend))
  343. } else {
  344. try requestHandler.sendStatus(ServerStatus(code: .ok,
  345. message: oddStatusMessage,
  346. trailingMetadata: trailingMetadataToSend))
  347. }
  348. }
  349. func handleServerStream(requestHandler: Handler) throws {
  350. XCTAssertEqual(requestHandler.host, host)
  351. XCTAssertEqual(requestHandler.method, helloServerStream)
  352. let initialMetadata = requestHandler.requestMetadata
  353. verify_metadata(initialMetadata, expected: initialClientMetadata)
  354. let initialMetadataToSend = try Metadata(initialServerMetadata)
  355. try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) {
  356. if let messageData = $0 {
  357. let messageString = String(data: messageData, encoding: .utf8)
  358. XCTAssertEqual(messageString, evenClientText)
  359. } else {
  360. XCTFail("handleServerStream message missing")
  361. }
  362. }
  363. let replyMessage = serverText.data(using: .utf8)!
  364. for _ in 0..<steps {
  365. try requestHandler.call.sendMessage(data: replyMessage) { error in
  366. XCTAssertNil(error)
  367. }
  368. requestHandler.call.messageQueueEmpty.wait()
  369. }
  370. let trailingMetadataToSend = try Metadata(trailingServerMetadata)
  371. try requestHandler.sendStatus(ServerStatus(
  372. // We need to return status OK here, as it seems like the server might never send out the last few messages once it
  373. // has been asked to send a non-OK status. Alternatively, we could send a non-OK status here, but then we would need
  374. // to sleep for a few milliseconds before sending the non-OK status.
  375. code: .ok,
  376. message: "Custom Status Message ServerStreaming",
  377. trailingMetadata: trailingMetadataToSend))
  378. }
  379. func handleBiDiStream(requestHandler: Handler) throws {
  380. XCTAssertEqual(requestHandler.host, host)
  381. XCTAssertEqual(requestHandler.method, helloBiDiStream)
  382. let initialMetadata = requestHandler.requestMetadata
  383. verify_metadata(initialMetadata, expected: initialClientMetadata)
  384. let initialMetadataToSend = try Metadata(initialServerMetadata)
  385. let sendMetadataSem = DispatchSemaphore(value: 0)
  386. try requestHandler.sendMetadata(initialMetadata: initialMetadataToSend) { _ in
  387. _ = sendMetadataSem.signal()
  388. }
  389. _ = sendMetadataSem.wait()
  390. // Receive remaining pings
  391. for _ in 0..<steps {
  392. let receiveSem = DispatchSemaphore(value: 0)
  393. try requestHandler.call.receiveMessage { callStatus in
  394. if let messageData = callStatus.resultData {
  395. let messageString = String(data: messageData, encoding: .utf8)
  396. XCTAssertEqual(messageString, clientPing)
  397. } else {
  398. XCTFail("handleBiDiStream message empty")
  399. }
  400. receiveSem.signal()
  401. }
  402. _ = receiveSem.wait()
  403. }
  404. // Send back pongs
  405. let replyMessage = serverPong.data(using: .utf8)!
  406. for _ in 0..<steps {
  407. try requestHandler.call.sendMessage(data: replyMessage) { error in
  408. XCTAssertNil(error)
  409. }
  410. requestHandler.call.messageQueueEmpty.wait()
  411. }
  412. let trailingMetadataToSend = try Metadata(trailingServerMetadata)
  413. let sem = DispatchSemaphore(value: 0)
  414. try requestHandler.sendStatus(ServerStatus(
  415. // We need to return status OK here, as it seems like the server might never send out the last few messages once it
  416. // has been asked to send a non-OK status. Alternatively, we could send a non-OK status here, but then we would need
  417. // to sleep for a few milliseconds before sending the non-OK status.
  418. code: .ok,
  419. message: "Custom Status Message BiDi",
  420. trailingMetadata: trailingMetadataToSend)) { sem.signal() }
  421. _ = sem.wait()
  422. }