/* * Copyright 2017, gRPC Authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import Commander import Dispatch import Foundation import gRPC // Common flags and options let sslFlag = Flag("ssl", description: "if true, use SSL for connections") func addressOption(_ address: String) -> Option { return Option("address", default: address, description: "address of server") } let portOption = Option("port", default: "8080", description: "port of server") let messageOption = Option("message", default: "Testing 1 2 3", description: "message to send") // Helper function for client actions func buildEchoService(_ ssl: Bool, _ address: String, _ port: String, _: String) -> Echo_EchoServiceClient { var service: Echo_EchoServiceClient if ssl { let certificateURL = URL(fileURLWithPath: "ssl.crt") let certificates = try! String(contentsOf: certificateURL) service = Echo_EchoServiceClient(address: address + ":" + port, certificates: certificates, host: "example.com") service.host = "example.com" } else { service = Echo_EchoServiceClient(address: address + ":" + port, secure: false) } service.metadata = Metadata([ "x-goog-api-key": "YOUR_API_KEY", "x-ios-bundle-identifier": "io.grpc.echo" ]) return service } Group { $0.command("serve", sslFlag, addressOption("0.0.0.0"), portOption, description: "Run an echo server.") { ssl, address, port in let sem = DispatchSemaphore(value: 0) let echoProvider = EchoProvider() var echoServer: Echo_EchoServer? if ssl { print("starting secure server") let certificateURL = URL(fileURLWithPath: "ssl.crt") let keyURL = URL(fileURLWithPath: "ssl.key") echoServer = Echo_EchoServer(address: address + ":" + port, certificateURL: certificateURL, keyURL: keyURL, provider: echoProvider) echoServer?.start() } else { print("starting insecure server") echoServer = Echo_EchoServer(address: address + ":" + port, provider: echoProvider) echoServer?.start() } // This blocks to keep the main thread from finishing while the server runs, // but the server never exits. Kill the process to stop it. _ = sem.wait() // This suppresses a "variable echoServer was written to, but never read" warning. _ = echoServer // And this ensures that echoServer doesn't get deallocated right after it is created. echoServer = nil } $0.command("get", sslFlag, addressOption("localhost"), portOption, messageOption, description: "Perform a unary get().") { ssl, address, port, message in print("calling get") let service = buildEchoService(ssl, address, port, message) var requestMessage = Echo_EchoRequest() requestMessage.text = message print("get sending: " + requestMessage.text) let responseMessage = try service.get(requestMessage) print("get received: " + responseMessage.text) } $0.command("expand", sslFlag, addressOption("localhost"), portOption, messageOption, description: "Perform a server-streaming expand().") { ssl, address, port, message in print("calling expand") let service = buildEchoService(ssl, address, port, message) var requestMessage = Echo_EchoRequest() requestMessage.text = message print("expand sending: " + requestMessage.text) let sem = DispatchSemaphore(value: 0) var callResult : CallResult? let expandCall = try service.expand(requestMessage) { result in callResult = result sem.signal() } var running = true while running { do { let responseMessage = try expandCall.receive() print("expand received: \(responseMessage.text)") } catch ClientError.endOfStream { running = false } } _ = sem.wait() if let statusCode = callResult?.statusCode { print("expand completed with code \(statusCode)") } } $0.command("collect", sslFlag, addressOption("localhost"), portOption, messageOption, description: "Perform a client-streaming collect().") { ssl, address, port, message in print("calling collect") let service = buildEchoService(ssl, address, port, message) let sem = DispatchSemaphore(value: 0) var callResult : CallResult? let collectCall = try service.collect { result in callResult = result sem.signal() } let sendCountMutex = Mutex() var sendCount = 0 let parts = message.components(separatedBy: " ") for part in parts { var requestMessage = Echo_EchoRequest() requestMessage.text = part print("collect sending: " + part) try collectCall.send(requestMessage) { error in sendCountMutex.synchronize { sendCount = sendCount + 1 } if let error = error { print("collect send error \(error)") } } } // don't close until all sends have completed var waiting = true while (waiting) { sendCountMutex.synchronize { if sendCount == parts.count { waiting = false } } } let responseMessage = try collectCall.closeAndReceive() print("collect received: \(responseMessage.text)") _ = sem.wait() if let statusCode = callResult?.statusCode { print("collect completed with status \(statusCode)") } } $0.command("update", sslFlag, addressOption("localhost"), portOption, messageOption, description: "Perform a bidirectional-streaming update().") { ssl, address, port, message in print("calling update") let service = buildEchoService(ssl, address, port, message) let sem = DispatchSemaphore(value: 0) var callResult : CallResult? let updateCall = try service.update { result in callResult = result sem.signal() } let responsesMutex = Mutex() var responses : [String] = [] DispatchQueue.global().async { var running = true while running { do { let responseMessage = try updateCall.receive() responsesMutex.synchronize { responses.append("update received: \(responseMessage.text)") } } catch ClientError.endOfStream { running = false } catch (let error) { responsesMutex.synchronize { responses.append("update receive error: \(error)") } } } } let parts = message.components(separatedBy: " ") for part in parts { var requestMessage = Echo_EchoRequest() requestMessage.text = part print("update sending: " + requestMessage.text) try updateCall.send(requestMessage) { error in if let error = error { print("update send error: \(error)") } } } // don't close until last update is received var waiting = true while (waiting) { responsesMutex.synchronize { if responses.count == parts.count { waiting = false } } } try updateCall.closeSend() _ = sem.wait() for response in responses { print(response) } if let statusCode = callResult?.statusCode { print("update completed with status \(statusCode)") } } }.run()