Browse Source

Add alternative API prototype for Echo example.

Tim Burks 9 years ago
parent
commit
5028338f04

+ 9 - 0
Samples/Echo-v2/Makefile

@@ -0,0 +1,9 @@
+LIBDIR = /usr/local/lib
+INCDIR = /usr/local/include
+
+all:
+	swift build -Xlinker -L$(LIBDIR) -Xlinker -lgrpc -Xcc -I$(INCDIR)
+
+clean :
+	rm -rf Packages
+	rm -rf .build

+ 40 - 0
Samples/Echo-v2/Package.swift

@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+import PackageDescription
+let package = Package (
+    name: "Echo",
+    dependencies: [
+        .Package(url: "https://github.com/grpc/grpc-swift.git", Version(0,1,0)),
+        .Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,24)),
+    ]
+)

+ 321 - 0
Samples/Echo-v2/Sources/EchoServer.swift

@@ -0,0 +1,321 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+import Foundation
+import gRPC
+import Darwin // for sleep()
+
+// This seemed like a nice idea but doesn't work because
+// specific message types are in the protocol signatures.
+// There are also functions in the Session classes that depend
+// on specific message types.
+
+protocol UnaryServer {
+  func handle(message:Echo_EchoRequest) -> Echo_EchoResponse?
+}
+
+protocol ServerStreamingServer {
+  func handle(session:ServerStreamingSession, message:Echo_EchoRequest) -> Void
+}
+
+protocol ClientStreamingServer {
+  func handle(session:ClientStreamingSession, message:Echo_EchoRequest) -> Void
+  func close(session:ClientStreamingSession)
+}
+
+protocol BidiStreamingServer {
+  func handle(session:BidiStreamingSession, message:Echo_EchoRequest) -> Void
+}
+
+// nonstreaming
+class UnarySession : Session {
+  var handler : Handler
+  var server : UnaryServer
+
+  init(handler:Handler, server: UnaryServer) {
+    self.handler = handler
+    self.server = server
+  }
+
+  func run() {
+    do {
+      try handler.receiveMessage(initialMetadata:Metadata()) {(requestData) in
+        if let requestData = requestData {
+          let requestMessage = try! Echo_EchoRequest(protobuf:requestData)
+          if let replyMessage = self.server.handle(message:requestMessage) { // calling stub
+            try self.handler.sendResponse(message:replyMessage.serializeProtobuf(),
+                                          statusCode: 0,
+                                          statusMessage: "OK",
+                                          trailingMetadata:Metadata())
+          }
+        }
+      }
+    } catch (let callError) {
+      print("grpc error: \(callError)")
+    }
+  }
+}
+
+// server streaming
+class ServerStreamingSession : Session {
+  var handler : Handler
+  var server : ServerStreamingServer
+
+  init(handler:Handler, server: ServerStreamingServer) {
+    self.handler = handler
+    self.server = server
+  }
+
+  func sendMessage(message:Echo_EchoResponse) -> Void {
+    try! handler.sendResponse(message:message.serializeProtobuf()) {}
+  }
+
+  func close() -> Void {
+    try! self.handler.sendStatus(statusCode:0,
+                                 statusMessage:"OK",
+                                 trailingMetadata:Metadata(),
+                                 completion:{
+
+    })
+  }
+
+  func run() {
+    do {
+      try handler.receiveMessage(initialMetadata:Metadata()) {(requestData) in
+        if let requestData = requestData {
+          let requestMessage = try! Echo_EchoRequest(protobuf:requestData)
+          self.server.handle(session: self, message:requestMessage)
+        }
+      }
+    } catch (let callError) {
+      print("grpc error: \(callError)")
+    }
+  }
+}
+
+// client streaming
+class ClientStreamingSession : Session {
+  var handler : Handler
+  var server : ClientStreamingServer
+
+  init(handler:Handler, server: ClientStreamingServer) {
+    self.handler = handler
+    self.server = server
+  }
+
+  func sendMessage(message:Echo_EchoResponse) -> Void {
+    try! self.handler.sendResponse(message:message.serializeProtobuf(),
+                                   statusCode: 0,
+                                   statusMessage: "OK",
+                                   trailingMetadata: Metadata())
+  }
+
+  func waitForMessage() {
+    do {
+      try handler.receiveMessage() {(requestData) in
+        if let requestData = requestData {
+
+          let requestMessage = try! Echo_EchoRequest(protobuf:requestData)
+          self.waitForMessage()
+          self.server.handle(session:self, message:requestMessage)
+
+        } else {
+          // if we get an empty message (requestData == nil), we close the connection
+          self.server.close(session:self)
+        }
+      }
+    } catch (let error) {
+      print(error)
+    }
+  }
+
+  func run() {
+    do {
+      try self.handler.sendMetadata(initialMetadata:Metadata()) {
+        self.waitForMessage()
+      }
+    } catch (let callError) {
+      print("grpc error: \(callError)")
+    }
+  }
+}
+
+// fully streaming
+class BidiStreamingSession : Session {
+  var handler : Handler
+  var server : BidiStreamingServer
+
+  init(handler:Handler, server: BidiStreamingServer) {
+    self.handler = handler
+    self.server = server
+  }
+
+  func sendMessage(message:Echo_EchoResponse) -> Void {
+    try! handler.sendResponse(message:message.serializeProtobuf()) {}
+  }
+
+  func waitForMessage() {
+    do {
+      try handler.receiveMessage() {(requestData) in
+        if let requestData = requestData {
+
+          let requestMessage = try! Echo_EchoRequest(protobuf:requestData)
+          self.waitForMessage()
+          self.server.handle(session:self, message:requestMessage)
+
+        } else {
+          // if we get an empty message (requestData == nil), we close the connection
+          try self.handler.sendStatus(statusCode: 0,
+                                      statusMessage: "OK",
+                                      trailingMetadata: Metadata())
+          {
+          }
+        }
+      }
+    } catch (let error) {
+      print(error)
+    }
+  }
+
+  func run() {
+    do {
+      try self.handler.sendMetadata(initialMetadata:Metadata()) {
+        self.waitForMessage()
+      }
+    } catch (let callError) {
+      print("grpc error: \(callError)")
+    }
+  }
+}
+
+class EchoServer {
+  private var address: String
+  private var server: Server
+
+  init(address:String, secure:Bool) {
+    gRPC.initialize()
+    self.address = address
+    if secure {
+      let certificateURL = URL(fileURLWithPath:"ssl.crt")
+      let certificate = try! String(contentsOf: certificateURL)
+      let keyURL = URL(fileURLWithPath:"ssl.key")
+      let key = try! String(contentsOf: keyURL)
+      self.server = gRPC.Server(address:address, key:key, certs:certificate)
+    } else {
+      self.server = gRPC.Server(address:address)
+    }
+  }
+
+  func start() {
+    print("Server Starting")
+    print("GRPC version " + gRPC.version())
+
+    server.run {(handler) in
+      print("Server received request to " + handler.host
+        + " calling " + handler.method
+        + " from " + handler.caller)
+
+      if (handler.method == "/echo.Echo/Get") {
+        handler.session = UnarySession(handler:handler,
+                                       server:EchoGetServer())
+        handler.session.run()
+      }
+
+      else if (handler.method == "/echo.Echo/Expand") {
+        handler.session = ServerStreamingSession(handler:handler,
+                                                 server:EchoExpandServer())
+        handler.session.run()
+      }
+
+      else if (handler.method == "/echo.Echo/Collect") {
+        handler.session = ClientStreamingSession(handler:handler,
+                                                 server:EchoCollectServer())
+        handler.session.run()
+      }
+
+      else if (handler.method == "/echo.Echo/Update") {
+        handler.session = BidiStreamingSession(handler:handler,
+                                               server:EchoUpdateServer())
+        handler.session.run()
+      }
+    }
+  }
+}
+
+// The following code is for developer/users to edit.
+// Everything above these lines is intended to be preexisting or generated.
+
+class EchoGetServer : UnaryServer {
+
+  func handle(message:Echo_EchoRequest) -> Echo_EchoResponse? {
+    return Echo_EchoResponse(text: "Swift echo get: " + message.text)
+  }
+}
+
+class EchoExpandServer : ServerStreamingServer {
+
+  func handle(session:ServerStreamingSession, message:Echo_EchoRequest) -> Void {
+    let parts = message.text.components(separatedBy: " ")
+    var i = 0
+    for part in parts {
+      session.sendMessage(message:Echo_EchoResponse(text:"Swift echo expand (\(i)): \(part)"))
+      i += 1
+      sleep(1)
+    }
+    session.close()
+  }
+}
+
+class EchoCollectServer : ClientStreamingServer {
+  var result = ""
+
+  func handle(session:ClientStreamingSession, message:Echo_EchoRequest) -> Void {
+    if result != "" {
+      result += " "
+    }
+    result += message.text
+  }
+
+  func close(session:ClientStreamingSession) {
+    session.sendMessage(message:Echo_EchoResponse(text:"Swift echo collect: " + result))
+  }
+}
+
+class EchoUpdateServer : BidiStreamingServer {
+  var i = 0
+
+  func handle(session:BidiStreamingSession, message:Echo_EchoRequest) -> Void {
+    session.sendMessage(message:Echo_EchoResponse(text:"Swift echo update (\(i)): \(message.text)"))
+    i += 1
+  }
+}
+

+ 286 - 0
Samples/Echo-v2/Sources/EchoService.swift

@@ -0,0 +1,286 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+import Foundation
+import gRPC
+
+// all code that follows is to-be-generated
+
+enum EchoResult {
+  case Response(r: Echo_EchoResponse)
+  case CallResult(c: CallResult)
+  case Error(s: String)
+}
+
+public class EchoGetCall {
+  var call : Call
+
+  init(_ call: Call) {
+    self.call = call
+  }
+
+  // Call this with the message to send,
+  // the callback will be called after the request is received.
+  func perform(request: Echo_EchoRequest,
+               callback:@escaping (EchoResult) -> Void)
+    -> Void {
+      let requestMessageData = try! request.serializeProtobuf()
+      let requestMetadata = Metadata()
+      try! call.perform(message: requestMessageData,
+                        metadata: requestMetadata)
+      {(callResult) in
+        print("Client received status \(callResult.statusCode) \(callResult.statusMessage!)")
+        if let messageData = callResult.resultData {
+          let responseMessage = try! Echo_EchoResponse(protobuf:messageData)
+          callback(EchoResult.Response(r: responseMessage))
+        } else {
+          callback(EchoResult.CallResult(c: callResult))
+        }
+      }
+  }
+}
+
+public class EchoExpandCall {
+  var call : Call
+
+  init(_ call: Call) {
+    self.call = call
+  }
+
+  // Call this once with the message to send,
+  // the callback will be called after the request is initiated.
+  func perform(request: Echo_EchoRequest,
+               callback:@escaping (CallResult) -> Void)
+    -> Void {
+      let requestMessageData = try! request.serializeProtobuf()
+      let requestMetadata = Metadata()
+      try! call.startServerStreaming(message: requestMessageData,
+                                     metadata: requestMetadata)
+      {(callResult) in
+        callback(callResult)
+      }
+  }
+
+  func Recv() -> EchoResult {
+    let done = NSCondition()
+    var result : EchoResult!
+    try! call.receiveMessage() {(data) in
+      if let data = data {
+        if let responseMessage = try? Echo_EchoResponse(protobuf:data) {
+          result = EchoResult.Response(r: responseMessage)
+        } else {
+          result = EchoResult.Error(s: "INVALID RESPONSE")
+        }
+      } else {
+        result = EchoResult.Error(s: "EOM")
+      }
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+    return result
+  }
+}
+
+public class EchoCollectCall {
+  var call : Call
+
+  init(_ call: Call) {
+    self.call = call
+  }
+
+  // Call this to start a call.
+  func start(metadata:Metadata, completion:@escaping (() -> Void)) throws {
+    try self.call.start(metadata: metadata, completion:completion)
+  }
+
+  // Call this to send each message in the request stream.
+  func Send(_ message: Echo_EchoRequest) {
+    let messageData = try! message.serializeProtobuf()
+    _ = call.sendMessage(data:messageData)
+  }
+
+  func CloseAndRecv() -> EchoResult {
+    let done = NSCondition()
+    var result : EchoResult!
+
+    try! self.receiveMessage() {(responseMessage) in
+      print("received")
+      if let responseMessage = responseMessage {
+        result = EchoResult.Response(r: responseMessage)
+      } else {
+        result = EchoResult.Error(s: "INVALID RESPONSE")
+      }
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+
+    try! self.close(completion:{})
+    done.lock()
+    done.wait()
+    done.unlock()
+    return result
+  }
+
+  // Call this to receive a message.
+  // The callback will be called when a message is received.
+  // call this again from the callback to wait for another message.
+  func receiveMessage(callback:@escaping (Echo_EchoResponse?) throws -> Void)
+    throws {
+      print("receiving message")
+      try call.receiveMessage() {(data) in
+        guard
+          let responseMessage = try? Echo_EchoResponse(protobuf:data)
+          else {
+            return
+        }
+        try callback(responseMessage)
+      }
+  }
+
+  func close(completion:@escaping (() -> Void)) throws {
+    print("closing")
+    try call.close(completion:completion)
+  }
+}
+
+public class EchoUpdateCall {
+  var call : Call
+
+  init(_ call: Call) {
+    self.call = call
+  }
+
+  func start(metadata:Metadata, completion:@escaping (() -> Void)) throws {
+    try self.call.start(metadata: metadata, completion:completion)
+  }
+
+  func receiveMessage(callback:@escaping (Echo_EchoResponse?) throws -> Void) throws {
+    try call.receiveMessage() {(data) in
+      if let data = data {
+        if let responseMessage = try? Echo_EchoResponse(protobuf:data) {
+          try callback(responseMessage)
+        } else {
+          try callback(nil) // error, bad data
+        }
+      } else {
+          try callback(nil)
+      }
+    }
+  }
+
+  func Recv() -> EchoResult {
+    let done = NSCondition()
+    var result : EchoResult!
+    try! self.receiveMessage() {responseMessage in
+      if let responseMessage = responseMessage {
+        result = EchoResult.Response(r: responseMessage)
+      } else {
+        result = EchoResult.Error(s: "EOM")
+      }
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+    return result
+  }
+
+  func Send(message:Echo_EchoRequest) {
+    let messageData = try! message.serializeProtobuf()
+    _ = call.sendMessage(data:messageData)
+  }
+
+  func CloseSend() {
+    let done = NSCondition()
+    try! call.close() {
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+  }
+}
+
+public class EchoService {
+  public var channel: Channel
+
+  public init(address: String) {
+    channel = Channel(address:address)
+  }
+
+  public init(address: String, certificates: String?, host: String?) {
+    channel = Channel(address:address, certificates:certificates, host:host)
+  }
+
+  func get(_ requestMessage: Echo_EchoRequest) -> EchoResult {
+    let call = EchoGetCall(channel.makeCall("/echo.Echo/Get"))
+    let done = NSCondition()
+    var finalResult : EchoResult!
+    call.perform(request:requestMessage) {(result) in
+      finalResult = result
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+    return finalResult
+  }
+
+  func expand(_ requestMessage: Echo_EchoRequest) -> EchoExpandCall {
+    let call = EchoExpandCall(channel.makeCall("/echo.Echo/Expand"))
+    call.perform(request:requestMessage) {response in }
+    return call
+  }
+
+  func collect() -> EchoCollectCall {
+    let call = EchoCollectCall(channel.makeCall("/echo.Echo/Collect"))
+    try! call.start(metadata:Metadata(), completion:{})
+    return call
+  }
+
+  func update() -> EchoUpdateCall {
+    let call = EchoUpdateCall(channel.makeCall("/echo.Echo/Update"))
+    try! call.start(metadata:Metadata(), completion:{})
+    return call
+  }
+}

+ 113 - 0
Samples/Echo-v2/Sources/echo.pb.swift

@@ -0,0 +1,113 @@
+/*
+ * DO NOT EDIT.
+ *
+ * Generated by the protocol buffer compiler.
+ * Source: echo.proto
+ *
+ */
+
+//  Copyright (c) 2015, Google Inc.
+// 
+//  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 Foundation
+import SwiftProtobuf
+
+
+public struct Echo_EchoRequest: ProtobufGeneratedMessage {
+  public var swiftClassName: String {return "Echo_EchoRequest"}
+  public var protoMessageName: String {return "EchoRequest"}
+  public var protoPackageName: String {return "echo"}
+  public var jsonFieldNames: [String: Int] {return [
+    "text": 1,
+  ]}
+  public var protoFieldNames: [String: Int] {return [
+    "text": 1,
+  ]}
+
+  public var text: String = ""
+
+  public init() {}
+
+  public init(text: String? = nil)
+  {
+    if let v = text {
+      self.text = v
+    }
+  }
+
+  public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
+    let handled: Bool
+    switch protoFieldNumber {
+    case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &text)
+    default:
+      handled = false
+    }
+    return handled
+  }
+
+  public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
+    if text != "" {
+      try visitor.visitSingularField(fieldType: ProtobufString.self, value: text, protoFieldNumber: 1, protoFieldName: "text", jsonFieldName: "text", swiftFieldName: "text")
+    }
+  }
+
+  public func _protoc_generated_isEqualTo(other: Echo_EchoRequest) -> Bool {
+    if text != other.text {return false}
+    return true
+  }
+}
+
+public struct Echo_EchoResponse: ProtobufGeneratedMessage {
+  public var swiftClassName: String {return "Echo_EchoResponse"}
+  public var protoMessageName: String {return "EchoResponse"}
+  public var protoPackageName: String {return "echo"}
+  public var jsonFieldNames: [String: Int] {return [
+    "text": 1,
+  ]}
+  public var protoFieldNames: [String: Int] {return [
+    "text": 1,
+  ]}
+
+  public var text: String = ""
+
+  public init() {}
+
+  public init(text: String? = nil)
+  {
+    if let v = text {
+      self.text = v
+    }
+  }
+
+  public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
+    let handled: Bool
+    switch protoFieldNumber {
+    case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &text)
+    default:
+      handled = false
+    }
+    return handled
+  }
+
+  public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
+    if text != "" {
+      try visitor.visitSingularField(fieldType: ProtobufString.self, value: text, protoFieldNumber: 1, protoFieldName: "text", jsonFieldName: "text", swiftFieldName: "text")
+    }
+  }
+
+  public func _protoc_generated_isEqualTo(other: Echo_EchoResponse) -> Bool {
+    if text != other.text {return false}
+    return true
+  }
+}

+ 219 - 0
Samples/Echo-v2/Sources/main.swift

@@ -0,0 +1,219 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+import Foundation
+import gRPC
+import QuickProto
+
+print("\(CommandLine.arguments)")
+
+// server options
+var server : Bool = false
+
+// client options
+var client : String = ""
+var message : String = "Testing 1 2 3"
+
+// general configuration
+var useSSL : Bool = false
+
+var i : Int = 0
+while i < Int(CommandLine.argc) {
+  let arg = CommandLine.arguments[i]
+  i = i + 1
+  if i == 1 {
+    continue // skip the first argument
+  }
+
+  if arg == "serve" {
+    server = true
+  } else if (arg == "get") || (arg == "expand") || (arg == "collect") || (arg == "update") {
+    client = arg
+  } else if arg == "-ssl" {
+    useSSL = true
+  } else if arg == "-m" && (i < Int(CommandLine.argc)) {
+    message = CommandLine.arguments[i]
+    i = i + 1
+  }
+}
+
+var done = NSCondition()
+
+gRPC.initialize()
+
+if server {
+  var echoServer: EchoServer!
+  if useSSL {
+    print("Starting secure server")
+    echoServer = EchoServer(address:"localhost:8443", secure:true)
+  } else {
+    print("Starting insecure server")
+    echoServer = EchoServer(address:"localhost:8081", secure:false)
+  }
+  echoServer.start()
+  // Block to keep the main thread from finishing while the server runs.
+  // This server never exits. Kill the process to stop it.
+  done.lock()
+  done.wait()
+  done.unlock()
+}
+
+if client != "" {
+  print("Starting client")
+
+  var service : EchoService
+  if useSSL {
+    let certificateURL = URL(fileURLWithPath:"ssl.crt")
+    let certificates = try! String(contentsOf: certificateURL)
+    service = EchoService(address:"localhost:8443", certificates:certificates, host:"example.com")
+    service.channel.host = "example.com" // sample override
+  } else {
+    service = EchoService(address:"localhost:8081")
+  }
+
+  let requestMetadata = Metadata(["x-goog-api-key":"YOUR_API_KEY",
+                                  "x-ios-bundle-identifier":"com.google.echo"])
+
+  // Unary
+  if client == "get" {
+    var requestMessage = Echo_EchoRequest(text:message)
+    print("Sending: " + requestMessage.text)
+    let result = service.get(requestMessage)
+    switch result {
+    case .Response(let responseMessage):
+      print("get received: " + responseMessage.text)
+    case .CallResult(let result):
+      print("get: no message received. \(result)")
+    case .Error(let error):
+      print("get: no message received. \(error)")
+    }
+  }
+
+  // Server streaming
+  if client == "expand" {
+    let requestMessage = Echo_EchoRequest(text:message)
+    print("Sending: " + requestMessage.text)
+    let expandCall = service.expand(requestMessage)
+    var running = true
+    while running {
+      let result = expandCall.Recv()
+      switch result {
+      case .Response(let responseMessage):
+        print("Received: \(responseMessage.text)")
+      case .CallResult(let result):
+        print("error: \(result)")
+      case .Error(let error):
+        if error == "EOM" {
+          print("expand closed")
+          running = false
+          break
+        } else {
+          print("error: \(error)")
+        }
+      }
+    }
+  }
+
+  // Client streaming
+  if client == "collect" {
+    let collectCall = service.collect()
+
+    let parts = message.components(separatedBy:" ")
+    for part in parts {
+      let requestMessage = Echo_EchoRequest(text:part)
+      print("Sending: " + part)
+      collectCall.Send(requestMessage)
+      sleep(1)
+    }
+
+    let result = collectCall.CloseAndRecv()
+    switch result {
+    case .Response(let responseMessage):
+      print("Received: \(responseMessage.text)")
+    case .CallResult(let result):
+      print("error: \(result)")
+    case .Error(let error):
+      if error == "EOM" {
+        print("collect closed")
+        break
+      } else {
+        print("error: \(error)")
+      }
+    }
+  }
+
+  // Bidirectional streaming
+  if client == "update" {
+    let updateCall = service.update()
+
+    DispatchQueue.global().async {
+      var running = true
+      while running {
+        let result = updateCall.Recv()
+        switch result {
+        case .Response(let responseMessage):
+          print("Received: \(responseMessage.text)")
+        case .CallResult(let result):
+          print("error: \(result)")
+        case .Error(let error):
+          if error == "EOM" {
+            print("update closed")
+            done.lock()
+            done.signal()
+            done.unlock()
+            break
+          } else {
+            print("error: \(error)")
+          }
+        }
+      }
+    }
+
+    let parts = message.components(separatedBy:" ")
+    for part in parts {
+      let requestMessage = Echo_EchoRequest(text:part)
+      print("Sending: " + requestMessage.text)
+      updateCall.Send(message:requestMessage)
+      sleep(1)
+    }
+    updateCall.CloseSend()
+
+    // Wait for the call to complete.
+    done.lock()
+    done.wait()
+    done.unlock()
+  }
+  
+}
+
+
+

+ 17 - 0
Samples/Echo-v2/ssl.crt

@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqDCCAZACCQC7L1EYvLe1FDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtl
+eGFtcGxlLmNvbTAeFw0xNjAzMzEyMTA3NDZaFw0xNzAzMzEyMTA3NDZaMBYxFDAS
+BgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAu2KXnItyilAByNapgqAlkOrLP7bcr4FJEuZH5LEu5yGkUT/swcZxKtuvLS5u
+5hU12gr27D9MbS7mSdBMFbayrWOf615vagf+D/ReTnPCvsn/4T+sQn3qdnSgIZJ5
+6dGFpLjk0aeEQX3kctUZxGEC8tUgkoP5W42zZvcEQR3EPyJr5plzzkw6t5ZEOQid
+i6xk0S7rNI2B6KoN78JoCJQr2JoYyrua1tjHB8OCHF+IXmXHhMOjfVgx1s0aqTdR
+BeYVUUrt9pKgjbnNE8W1E+ur0GkCkBhWEf+VoMq6BzpUe5H5L7lFJTaHkORvrlqt
+uzMhM610ecQnXTUconCkHdPPWwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCNoKg/
+lkYiiJqabbNe18Ja+El3qCPWNgDrS13s1Q8MsBZ1db+SQL4r6bHARtzI7SJ7uyrP
+r9YGq1q11uMJ8CGDJXJkeaK003pR1wYfruRhjulV0u3Q+9IRAezEsJbiKddNNnHv
+rQFtK/DQIk0aokp359mWnsRwon1zuFY4lMyPV9MonLbU91ZlnxyRuuIWc5fPrviT
+Mvo163V/B9N6qhZFzrCKl4p7iDqHuSRDGoGvvx6JBDOnJH/JFcmPv4oIqIXsbefu
+hRWMFf2GJI52r1Gj53Vr8SKgfWdBJ14BXXDP9/QQgKuM4b3Tcv38PQeGuwQ+jVqW
+fBonjE+i2znSdOH1
+-----END CERTIFICATE-----

+ 27 - 0
Samples/Echo-v2/ssl.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAu2KXnItyilAByNapgqAlkOrLP7bcr4FJEuZH5LEu5yGkUT/s
+wcZxKtuvLS5u5hU12gr27D9MbS7mSdBMFbayrWOf615vagf+D/ReTnPCvsn/4T+s
+Qn3qdnSgIZJ56dGFpLjk0aeEQX3kctUZxGEC8tUgkoP5W42zZvcEQR3EPyJr5plz
+zkw6t5ZEOQidi6xk0S7rNI2B6KoN78JoCJQr2JoYyrua1tjHB8OCHF+IXmXHhMOj
+fVgx1s0aqTdRBeYVUUrt9pKgjbnNE8W1E+ur0GkCkBhWEf+VoMq6BzpUe5H5L7lF
+JTaHkORvrlqtuzMhM610ecQnXTUconCkHdPPWwIDAQABAoIBADlxHbzVWoVfxUxF
+0azDXwCvmSKs8bWzUi1C/mLylcgwnehySieUp3hV47tb8o8BjYKLvenp9Ym6yMlz
+2v8FBHz2fz1ts4WzcYR+irJN9jL6RUBNfobbhpZNZhEkj87HdcprC2nhij9xUiiJ
+ft6eRoMeJmADqNsR8x7rNhioAVLAufORDGa5ThpaLTe53C7gzMcHerxBT7ZRCKqS
+J8EfxYHRF5k4VM0x2Xyo2DCIvoSP+ydcKiyuHQMAIbLt0GVZ+jOUQ/JJh+PNXGm6
+HJnv6O4+WB+O+D6TzxXkjeo8MBKyJCHHxTUCRMkU6ltPihvEzuGD+9M+amz5SK4G
+gxfxOyECgYEA6f93N5ASd2OE/cMNBSbsImZWFwfjcNrDX9V5QfoJ4zXPE7/e2PO/
+tfNrW7rm35RkygRzI/s7vVHoUuA6nGkeHOXUrOwiBybMk+PAflEwwdQNtXt4BJTZ
+mRIskcQGnJAITjw1zc8o9/V0tOZNOpbVeCR56ucHwxA+215CO4Y4jekCgYEAzQEd
+swh0kpRP+X8Xwfnnb0yXt4HCWsM4OHetz79JcOhv0LENj7zwJ3L3k0MOMZpkFKz/
+ty7SS/u3lgY1hVkJUggjw57Y9ZdIJvopx/myCLgXOaLyo24PdjpRJKIvOqYC1wQu
+o3D43pgtmjVVu8i1f/+X4akIURetA7pkeWGZVKMCgYAuDjUFv5qS2wia9aADapTB
+dIjvQYM3fCdGHnseTDtT+AxI49PVuav7AO0ZgeDdEpT/2f5bj6BDc/KZFT8T9/CQ
+WYARhOxxoeZUGViSxCInlDgahzGpHS7y3Mve6MkwWXz5AQrJ9kMnAq20yTtcE8Hy
+QqOoY054yyLEBHpewt0wuQKBgQCzusO//6y8Cb1n3u4ESUWHRZ5J60Bq9HZowzwm
+Q+1uSMonK+LY3uupmljFyec6w8H0gouanTkQFrqok/7+TsYmHi7ExZIvFpfSXEaf
+JSHaFRN/m4WglNCHda9IL8y6XWtl+SuubVAzTzXD2fi1Ls05T+tnkxtQhTJRb2vB
+IzkbgwKBgCuymXXQ41EzdJQoTWDe8CFLrP5ynKeaKzMN32ppUMbgb+DF4L9UpDm1
+CgckZ3THQEuh7yReY/2bU+XHK/GCdheNes+CXxu4dgSaVL/LONO08RuneDK1kQ5o
+e1oy4f+51rbwNGoUQ73U+cm/LSFJOir5EuNenpVZeMeQMktkg806
+-----END RSA PRIVATE KEY-----