main.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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 Commander
  17. import Dispatch
  18. import Foundation
  19. import NIO
  20. import NIOSSL
  21. import SwiftGRPCNIO
  22. // Common flags and options
  23. let sslFlag = Flag("ssl", description: "if true, use SSL for connections")
  24. func addressOption(_ address: String) -> Option<String> {
  25. return Option("address", default: address, description: "address of server")
  26. }
  27. let portOption = Option("port", default: 8080)
  28. let messageOption = Option("message",
  29. default: "Testing 1 2 3",
  30. description: "message to send")
  31. func makeClientTLSConfiguration() throws -> TLSConfiguration {
  32. let certificate = try NIOSSLCertificate(file: "ssl.crt", format: .pem)
  33. // The certificate common name is "example.com", so skip hostname verification.
  34. return .forClient(certificateVerification: .noHostnameVerification,
  35. trustRoots: .certificates([certificate]))
  36. }
  37. func makeServerTLSConfiguration() throws -> TLSConfiguration {
  38. let certificate = try NIOSSLCertificate(file: "ssl.crt", format: .pem)
  39. let key = try NIOSSLPrivateKey(file: "ssl.key", format: .pem)
  40. return .forServer(certificateChain: [.certificate(certificate)],
  41. privateKey: .privateKey(key),
  42. trustRoots: .certificates([certificate]))
  43. }
  44. func makeClientTLS(enabled: Bool) throws -> GRPCClient.TLSMode {
  45. guard enabled else {
  46. return .none
  47. }
  48. return .custom(try NIOSSLContext(configuration: try makeClientTLSConfiguration()))
  49. }
  50. func makeServerTLS(enabled: Bool) throws -> GRPCServer.TLSMode {
  51. guard enabled else {
  52. return .none
  53. }
  54. return .custom(try NIOSSLContext(configuration: try makeServerTLSConfiguration()))
  55. }
  56. /// Create en `EchoClient` and wait for it to initialize. Returns nil if initialisation fails.
  57. func makeEchoClient(address: String, port: Int, ssl: Bool) -> Echo_EchoService_NIOClient? {
  58. let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  59. do {
  60. return try GRPCClient.start(host: address, port: port, eventLoopGroup: eventLoopGroup, tls: try makeClientTLS(enabled: ssl))
  61. .map { client in Echo_EchoService_NIOClient(client: client) }
  62. .wait()
  63. } catch {
  64. print("Unable to create an EchoClient: \(error)")
  65. return nil
  66. }
  67. }
  68. Group {
  69. $0.command("serve",
  70. sslFlag,
  71. addressOption("localhost"),
  72. portOption,
  73. description: "Run an echo server.") { ssl, address, port in
  74. let sem = DispatchSemaphore(value: 0)
  75. let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  76. print(ssl ? "starting secure server" : "starting insecure server")
  77. _ = try! GRPCServer.start(hostname: address,
  78. port: port,
  79. eventLoopGroup: eventLoopGroup,
  80. serviceProviders: [EchoProviderNIO()],
  81. tls: makeServerTLS(enabled: ssl))
  82. .wait()
  83. // This blocks to keep the main thread from finishing while the server runs,
  84. // but the server never exits. Kill the process to stop it.
  85. _ = sem.wait()
  86. }
  87. $0.command(
  88. "get",
  89. sslFlag,
  90. addressOption("localhost"),
  91. portOption,
  92. messageOption,
  93. description: "Perform a unary get()."
  94. ) { ssl, address, port, message in
  95. print("calling get")
  96. guard let echo = makeEchoClient(address: address, port: port, ssl: ssl) else { return }
  97. var requestMessage = Echo_EchoRequest()
  98. requestMessage.text = message
  99. print("get sending: \(requestMessage.text)")
  100. let get = echo.get(requestMessage)
  101. get.response.whenSuccess { response in
  102. print("get received: \(response.text)")
  103. }
  104. get.response.whenFailure { error in
  105. print("get response failed with error: \(error)")
  106. }
  107. // wait() on the status to stop the program from exiting.
  108. do {
  109. let status = try get.status.wait()
  110. print("get completed with status: \(status)")
  111. } catch {
  112. print("get status failed with error: \(error)")
  113. }
  114. }
  115. $0.command(
  116. "expand",
  117. sslFlag,
  118. addressOption("localhost"),
  119. portOption,
  120. messageOption,
  121. description: "Perform a server-streaming expand()."
  122. ) { ssl, address, port, message in
  123. print("calling expand")
  124. guard let echo = makeEchoClient(address: address, port: port, ssl: ssl) else { return }
  125. let requestMessage = Echo_EchoRequest.with { $0.text = message }
  126. print("expand sending: \(requestMessage.text)")
  127. let expand = echo.expand(requestMessage) { response in
  128. print("expand received: \(response.text)")
  129. }
  130. // wait() on the status to stop the program from exiting.
  131. do {
  132. let status = try expand.status.wait()
  133. print("expand completed with status: \(status)")
  134. } catch {
  135. print("expand status failed with error: \(error)")
  136. }
  137. }
  138. $0.command(
  139. "collect",
  140. sslFlag,
  141. addressOption("localhost"),
  142. portOption,
  143. messageOption,
  144. description: "Perform a client-streaming collect()."
  145. ) { ssl, address, port, message in
  146. print("calling collect")
  147. guard let echo = makeEchoClient(address: address, port: port, ssl: ssl) else { return }
  148. let collect = echo.collect()
  149. var queue = collect.newMessageQueue()
  150. for part in message.components(separatedBy: " ") {
  151. var requestMessage = Echo_EchoRequest()
  152. requestMessage.text = part
  153. print("collect sending: \(requestMessage.text)")
  154. queue = queue.flatMap { collect.sendMessage(requestMessage) }
  155. }
  156. queue.whenSuccess { collect.sendEnd(promise: nil) }
  157. collect.response.whenSuccess { respone in
  158. print("collect received: \(respone.text)")
  159. }
  160. collect.response.whenFailure { error in
  161. print("collect response failed with error: \(error)")
  162. }
  163. // wait() on the status to stop the program from exiting.
  164. do {
  165. let status = try collect.status.wait()
  166. print("collect completed with status: \(status)")
  167. } catch {
  168. print("collect status failed with error: \(error)")
  169. }
  170. }
  171. $0.command(
  172. "update",
  173. sslFlag,
  174. addressOption("localhost"),
  175. portOption,
  176. messageOption,
  177. description: "Perform a bidirectional-streaming update()."
  178. ) { ssl, address, port, message in
  179. print("calling update")
  180. guard let echo = makeEchoClient(address: address, port: port, ssl: ssl) else { return }
  181. let update = echo.update { response in
  182. print("update received: \(response.text)")
  183. }
  184. var queue = update.newMessageQueue()
  185. for part in message.components(separatedBy: " ") {
  186. var requestMessage = Echo_EchoRequest()
  187. requestMessage.text = part
  188. print("update sending: \(requestMessage.text)")
  189. queue = queue.flatMap { update.sendMessage(requestMessage) }
  190. }
  191. queue.whenSuccess { update.sendEnd(promise: nil) }
  192. // wait() on the status to stop the program from exiting.
  193. do {
  194. let status = try update.status.wait()
  195. print("update completed with status: \(status)")
  196. } catch {
  197. print("update status failed with error: \(error)")
  198. }
  199. }
  200. }.run()