main.swift 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 EchoImplementation
  17. import EchoModel
  18. import Foundation
  19. import GRPC
  20. import GRPCSampleData
  21. import Logging
  22. import NIO
  23. import NIOSSL
  24. // MARK: - Argument parsing
  25. enum RPC: String {
  26. case get
  27. case collect
  28. case expand
  29. case update
  30. }
  31. enum Command {
  32. case server(port: Int, useTLS: Bool)
  33. case client(port: Int, useTLS: Bool, rpc: RPC, message: String, useInterceptors: Bool)
  34. init?(from args: [String]) {
  35. guard !args.isEmpty else {
  36. return nil
  37. }
  38. var args = args
  39. switch args.removeFirst() {
  40. case "server":
  41. guard args.count == 1 || args.count == 2,
  42. let port = args.popLast().flatMap(Int.init),
  43. let useTLS = Command.parseTLSArg(args.popLast())
  44. else {
  45. return nil
  46. }
  47. self = .server(port: port, useTLS: useTLS)
  48. case "client":
  49. guard (3 ... 5).contains(args.count),
  50. let message = args.popLast(),
  51. let rpc = args.popLast().flatMap(RPC.init),
  52. let port = args.popLast().flatMap(Int.init)
  53. else {
  54. return nil
  55. }
  56. var useTLS = false
  57. var useInterceptors = false
  58. while let arg = args.popLast() {
  59. if let tls = Command.parseTLSArg(arg) {
  60. useTLS = tls
  61. } else if arg == "--intercept" {
  62. useInterceptors = true
  63. }
  64. }
  65. self = .client(
  66. port: port,
  67. useTLS: useTLS,
  68. rpc: rpc,
  69. message: message,
  70. useInterceptors: useInterceptors
  71. )
  72. default:
  73. return nil
  74. }
  75. }
  76. private static func parseTLSArg(_ arg: String?) -> Bool? {
  77. switch arg {
  78. case .some("--tls"):
  79. return true
  80. case .none, .some("--notls"):
  81. return false
  82. default:
  83. return nil
  84. }
  85. }
  86. }
  87. func printUsageAndExit(program: String) -> Never {
  88. print("""
  89. Usage: \(program) COMMAND [OPTIONS...]
  90. Commands:
  91. server [--tls|--notls] PORT Starts the echo server on the given port.
  92. client [--tls|--notls] [--intercept] PORT RPC MESSAGE
  93. Connects to the echo server running on localhost
  94. and the given port and calls the RPC with the
  95. provided message. See below for a list of
  96. possible RPCs.
  97. RPCs:
  98. * get (unary)
  99. * collect (client streaming)
  100. * expand (server streaming)
  101. * update (bidirectional streaming)
  102. """)
  103. exit(1)
  104. }
  105. func main(args: [String]) {
  106. var args = args
  107. let program = args.removeFirst()
  108. guard let command = Command(from: args) else {
  109. printUsageAndExit(program: program)
  110. }
  111. // Okay, we're nearly ready to start, create an `EventLoopGroup` most suitable for our platform.
  112. let group = PlatformSupport.makeEventLoopGroup(loopCount: 1)
  113. defer {
  114. try! group.syncShutdownGracefully()
  115. }
  116. // Now run the server/client.
  117. switch command {
  118. case let .server(port: port, useTLS: useTLS):
  119. do {
  120. try startEchoServer(group: group, port: port, useTLS: useTLS)
  121. } catch {
  122. print("Error running server: \(error)")
  123. }
  124. case let .client(port, useTLS, rpc, message, useInterceptor):
  125. let client = makeClient(
  126. group: group,
  127. port: port,
  128. useTLS: useTLS,
  129. useInterceptor: useInterceptor
  130. )
  131. defer {
  132. try! client.channel.close().wait()
  133. }
  134. callRPC(rpc, using: client, message: message)
  135. }
  136. }
  137. // MARK: - Server / Client
  138. func startEchoServer(group: EventLoopGroup, port: Int, useTLS: Bool) throws {
  139. let builder: Server.Builder
  140. if useTLS {
  141. // We're using some self-signed certs here: check they aren't expired.
  142. let caCert = SampleCertificate.ca
  143. let serverCert = SampleCertificate.server
  144. precondition(
  145. !caCert.isExpired && !serverCert.isExpired,
  146. "SSL certificates are expired. Please submit an issue at https://github.com/grpc/grpc-swift."
  147. )
  148. builder = Server.secure(
  149. group: group,
  150. certificateChain: [serverCert.certificate],
  151. privateKey: SamplePrivateKey.server
  152. )
  153. .withTLS(trustRoots: .certificates([caCert.certificate]))
  154. print("starting secure server")
  155. } else {
  156. print("starting insecure server")
  157. builder = Server.insecure(group: group)
  158. }
  159. let server = try builder.withServiceProviders([EchoProvider()])
  160. .bind(host: "localhost", port: port)
  161. .wait()
  162. print("started server: \(server.channel.localAddress!)")
  163. // This blocks to keep the main thread from finishing while the server runs,
  164. // but the server never exits. Kill the process to stop it.
  165. try server.onClose.wait()
  166. }
  167. func makeClient(
  168. group: EventLoopGroup,
  169. port: Int,
  170. useTLS: Bool,
  171. useInterceptor: Bool
  172. ) -> Echo_EchoClient {
  173. let builder: ClientConnection.Builder
  174. if useTLS {
  175. // We're using some self-signed certs here: check they aren't expired.
  176. let caCert = SampleCertificate.ca
  177. let clientCert = SampleCertificate.client
  178. precondition(
  179. !caCert.isExpired && !clientCert.isExpired,
  180. "SSL certificates are expired. Please submit an issue at https://github.com/grpc/grpc-swift."
  181. )
  182. builder = ClientConnection.secure(group: group)
  183. .withTLS(certificateChain: [clientCert.certificate])
  184. .withTLS(privateKey: SamplePrivateKey.client)
  185. .withTLS(trustRoots: .certificates([caCert.certificate]))
  186. } else {
  187. builder = ClientConnection.insecure(group: group)
  188. }
  189. // Start the connection and create the client:
  190. let connection = builder.connect(host: "localhost", port: port)
  191. return Echo_EchoClient(
  192. channel: connection,
  193. interceptors: useInterceptor ? ExampleClientInterceptorFactory() : nil
  194. )
  195. }
  196. func callRPC(_ rpc: RPC, using client: Echo_EchoClient, message: String) {
  197. do {
  198. switch rpc {
  199. case .get:
  200. try echoGet(client: client, message: message)
  201. case .collect:
  202. try echoCollect(client: client, message: message)
  203. case .expand:
  204. try echoExpand(client: client, message: message)
  205. case .update:
  206. try echoUpdate(client: client, message: message)
  207. }
  208. } catch {
  209. print("\(rpc) RPC failed: \(error)")
  210. }
  211. }
  212. func echoGet(client: Echo_EchoClient, message: String) throws {
  213. // Get is a unary call.
  214. let get = client.get(.with { $0.text = message })
  215. // Register a callback for the response:
  216. get.response.whenComplete { result in
  217. switch result {
  218. case let .success(response):
  219. print("get receieved: \(response.text)")
  220. case let .failure(error):
  221. print("get failed with error: \(error)")
  222. }
  223. }
  224. // wait() for the call to terminate
  225. let status = try get.status.wait()
  226. print("get completed with status: \(status.code)")
  227. }
  228. func echoCollect(client: Echo_EchoClient, message: String) throws {
  229. // Collect is a client streaming call
  230. let collect = client.collect()
  231. // Split the messages and map them into requests
  232. let messages = message.components(separatedBy: " ").map { part in
  233. Echo_EchoRequest.with { $0.text = part }
  234. }
  235. // Stream the to the service (this can also be done on individual requests using `sendMessage`).
  236. collect.sendMessages(messages, promise: nil)
  237. // Close the request stream.
  238. collect.sendEnd(promise: nil)
  239. // Register a callback for the response:
  240. collect.response.whenComplete { result in
  241. switch result {
  242. case let .success(response):
  243. print("collect receieved: \(response.text)")
  244. case let .failure(error):
  245. print("collect failed with error: \(error)")
  246. }
  247. }
  248. // wait() for the call to terminate
  249. let status = try collect.status.wait()
  250. print("collect completed with status: \(status.code)")
  251. }
  252. func echoExpand(client: Echo_EchoClient, message: String) throws {
  253. // Expand is a server streaming call; provide a response handler.
  254. let expand = client.expand(.with { $0.text = message }) { response in
  255. print("expand received: \(response.text)")
  256. }
  257. // wait() for the call to terminate
  258. let status = try expand.status.wait()
  259. print("expand completed with status: \(status.code)")
  260. }
  261. func echoUpdate(client: Echo_EchoClient, message: String) throws {
  262. // Update is a bidirectional streaming call; provide a response handler.
  263. let update = client.update { response in
  264. print("update received: \(response.text)")
  265. }
  266. // Split the messages and map them into requests
  267. let messages = message.components(separatedBy: " ").map { part in
  268. Echo_EchoRequest.with { $0.text = part }
  269. }
  270. // Stream the to the service (this can also be done on individual requests using `sendMessage`).
  271. update.sendMessages(messages, promise: nil)
  272. // Close the request stream.
  273. update.sendEnd(promise: nil)
  274. // wait() for the call to terminate
  275. let status = try update.status.wait()
  276. print("update completed with status: \(status.code)")
  277. }
  278. main(args: CommandLine.arguments)