main.swift 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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 ArgumentParser
  17. import EchoImplementation
  18. import EchoModel
  19. import Foundation
  20. import GRPC
  21. import GRPCSampleData
  22. import Logging
  23. import NIO
  24. import NIOSSL
  25. // MARK: - Argument parsing
  26. enum RPC: String, ExpressibleByArgument {
  27. case get
  28. case collect
  29. case expand
  30. case update
  31. }
  32. struct Echo: ParsableCommand {
  33. static var configuration = CommandConfiguration(
  34. abstract: "An example to run and call a simple gRPC service for echoing messages.",
  35. subcommands: [Server.self, Client.self]
  36. )
  37. struct Server: ParsableCommand {
  38. static var configuration = CommandConfiguration(
  39. abstract: "Start a gRPC server providing the Echo service."
  40. )
  41. @Option(help: "The port to listen on for new connections")
  42. var port = 1234
  43. @Flag(help: "Whether TLS should be used or not")
  44. var tls = false
  45. func run() throws {
  46. let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  47. defer {
  48. try! group.syncShutdownGracefully()
  49. }
  50. do {
  51. try startEchoServer(group: group, port: self.port, useTLS: self.tls)
  52. } catch {
  53. print("Error running server: \(error)")
  54. }
  55. }
  56. }
  57. struct Client: ParsableCommand {
  58. static var configuration = CommandConfiguration(
  59. abstract: "Calls an RPC on the Echo server."
  60. )
  61. @Option(help: "The port to connect to")
  62. var port = 1234
  63. @Flag(help: "Whether TLS should be used or not")
  64. var tls = false
  65. @Flag(help: "Whether interceptors should be used, see 'docs/interceptors-tutorial.md'.")
  66. var intercept = false
  67. @Option(help: "RPC to call ('get', 'collect', 'expand', 'update').")
  68. var rpc: RPC = .get
  69. @Option(help: "How many RPCs to do.")
  70. var iterations: Int = 1
  71. @Argument(help: "Message to echo")
  72. var message: String
  73. func run() throws {
  74. let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  75. defer {
  76. try! group.syncShutdownGracefully()
  77. }
  78. let client = makeClient(
  79. group: group,
  80. port: self.port,
  81. useTLS: self.tls,
  82. useInterceptor: self.intercept
  83. )
  84. defer {
  85. try! client.channel.close().wait()
  86. }
  87. for _ in 0 ..< self.iterations {
  88. callRPC(self.rpc, using: client, message: self.message)
  89. }
  90. }
  91. }
  92. }
  93. // MARK: - Server / Client
  94. func startEchoServer(group: EventLoopGroup, port: Int, useTLS: Bool) throws {
  95. let builder: Server.Builder
  96. if useTLS {
  97. // We're using some self-signed certs here: check they aren't expired.
  98. let caCert = SampleCertificate.ca
  99. let serverCert = SampleCertificate.server
  100. precondition(
  101. !caCert.isExpired && !serverCert.isExpired,
  102. "SSL certificates are expired. Please submit an issue at https://github.com/grpc/grpc-swift."
  103. )
  104. builder = Server.usingTLSBackedByNIOSSL(
  105. on: group,
  106. certificateChain: [serverCert.certificate],
  107. privateKey: SamplePrivateKey.server
  108. )
  109. .withTLS(trustRoots: .certificates([caCert.certificate]))
  110. print("starting secure server")
  111. } else {
  112. print("starting insecure server")
  113. builder = Server.insecure(group: group)
  114. }
  115. let server = try builder.withServiceProviders([EchoProvider()])
  116. .bind(host: "localhost", port: port)
  117. .wait()
  118. print("started server: \(server.channel.localAddress!)")
  119. // This blocks to keep the main thread from finishing while the server runs,
  120. // but the server never exits. Kill the process to stop it.
  121. try server.onClose.wait()
  122. }
  123. func makeClient(
  124. group: EventLoopGroup,
  125. port: Int,
  126. useTLS: Bool,
  127. useInterceptor: Bool
  128. ) -> Echo_EchoClient {
  129. let builder: ClientConnection.Builder
  130. if useTLS {
  131. // We're using some self-signed certs here: check they aren't expired.
  132. let caCert = SampleCertificate.ca
  133. let clientCert = SampleCertificate.client
  134. precondition(
  135. !caCert.isExpired && !clientCert.isExpired,
  136. "SSL certificates are expired. Please submit an issue at https://github.com/grpc/grpc-swift."
  137. )
  138. builder = ClientConnection.usingTLSBackedByNIOSSL(on: group)
  139. .withTLS(certificateChain: [clientCert.certificate])
  140. .withTLS(privateKey: SamplePrivateKey.client)
  141. .withTLS(trustRoots: .certificates([caCert.certificate]))
  142. } else {
  143. builder = ClientConnection.insecure(group: group)
  144. }
  145. // Start the connection and create the client:
  146. let connection = builder.connect(host: "localhost", port: port)
  147. return Echo_EchoClient(
  148. channel: connection,
  149. interceptors: useInterceptor ? ExampleClientInterceptorFactory() : nil
  150. )
  151. }
  152. func callRPC(_ rpc: RPC, using client: Echo_EchoClient, message: String) {
  153. do {
  154. switch rpc {
  155. case .get:
  156. try echoGet(client: client, message: message)
  157. case .collect:
  158. try echoCollect(client: client, message: message)
  159. case .expand:
  160. try echoExpand(client: client, message: message)
  161. case .update:
  162. try echoUpdate(client: client, message: message)
  163. }
  164. } catch {
  165. print("\(rpc) RPC failed: \(error)")
  166. }
  167. }
  168. func echoGet(client: Echo_EchoClient, message: String) throws {
  169. // Get is a unary call.
  170. let get = client.get(.with { $0.text = message })
  171. // Register a callback for the response:
  172. get.response.whenComplete { result in
  173. switch result {
  174. case let .success(response):
  175. print("get receieved: \(response.text)")
  176. case let .failure(error):
  177. print("get failed with error: \(error)")
  178. }
  179. }
  180. // wait() for the call to terminate
  181. let status = try get.status.wait()
  182. print("get completed with status: \(status.code)")
  183. }
  184. func echoCollect(client: Echo_EchoClient, message: String) throws {
  185. // Collect is a client streaming call
  186. let collect = client.collect()
  187. // Split the messages and map them into requests
  188. let messages = message.components(separatedBy: " ").map { part in
  189. Echo_EchoRequest.with { $0.text = part }
  190. }
  191. // Stream the to the service (this can also be done on individual requests using `sendMessage`).
  192. collect.sendMessages(messages, promise: nil)
  193. // Close the request stream.
  194. collect.sendEnd(promise: nil)
  195. // Register a callback for the response:
  196. collect.response.whenComplete { result in
  197. switch result {
  198. case let .success(response):
  199. print("collect receieved: \(response.text)")
  200. case let .failure(error):
  201. print("collect failed with error: \(error)")
  202. }
  203. }
  204. // wait() for the call to terminate
  205. let status = try collect.status.wait()
  206. print("collect completed with status: \(status.code)")
  207. }
  208. func echoExpand(client: Echo_EchoClient, message: String) throws {
  209. // Expand is a server streaming call; provide a response handler.
  210. let expand = client.expand(.with { $0.text = message }) { response in
  211. print("expand received: \(response.text)")
  212. }
  213. // wait() for the call to terminate
  214. let status = try expand.status.wait()
  215. print("expand completed with status: \(status.code)")
  216. }
  217. func echoUpdate(client: Echo_EchoClient, message: String) throws {
  218. // Update is a bidirectional streaming call; provide a response handler.
  219. let update = client.update { response in
  220. print("update received: \(response.text)")
  221. }
  222. // Split the messages and map them into requests
  223. let messages = message.components(separatedBy: " ").map { part in
  224. Echo_EchoRequest.with { $0.text = part }
  225. }
  226. // Stream the to the service (this can also be done on individual requests using `sendMessage`).
  227. update.sendMessages(messages, promise: nil)
  228. // Close the request stream.
  229. update.sendEnd(promise: nil)
  230. // wait() for the call to terminate
  231. let status = try update.status.wait()
  232. print("update completed with status: \(status.code)")
  233. }
  234. Echo.main()