Skip to content

Instantly share code, notes, and snippets.

@MrMage
Created February 23, 2018 18:59
Show Gist options
  • Save MrMage/cbd6277d3c4e51179cae2798e70b2112 to your computer and use it in GitHub Desktop.
Save MrMage/cbd6277d3c4e51179cae2798e70b2112 to your computer and use it in GitHub Desktop.
Example for generated code without a package name set
/*
* DO NOT EDIT.
*
* Generated by the protocol buffer compiler.
* Source: echo.proto
*
*/
/*
* Copyright 2018, 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 Foundation
import Dispatch
import gRPC
import SwiftProtobuf
/// Type for errors thrown from generated client code.
internal enum EchoClientError : Error {
case endOfStream
case invalidMessageReceived
case error(c: CallResult)
}
/// Get (Unary)
internal final class EchoGetCall {
private var call : Call
/// Create a call.
fileprivate init(_ channel: Channel) {
self.call = channel.makeCall("/Echo/Get")
}
/// Run the call. Blocks until the reply is received.
/// - Throws: `BinaryEncodingError` if encoding fails. `CallError` if fails to call. `EchoClientError` if receives no response.
fileprivate func run(request: EchoRequest,
metadata: Metadata) throws -> EchoResponse {
let sem = DispatchSemaphore(value: 0)
var returnCallResult : CallResult!
var returnResponse : EchoResponse?
_ = try start(request:request, metadata:metadata) {response, callResult in
returnResponse = response
returnCallResult = callResult
sem.signal()
}
_ = sem.wait(timeout: DispatchTime.distantFuture)
if let returnResponse = returnResponse {
return returnResponse
} else {
throw EchoClientError.error(c: returnCallResult)
}
}
/// Start the call. Nonblocking.
/// - Throws: `BinaryEncodingError` if encoding fails. `CallError` if fails to call.
fileprivate func start(request: EchoRequest,
metadata: Metadata,
completion: @escaping (EchoResponse?, CallResult)->())
throws -> EchoGetCall {
let requestData = try request.serializedData()
try call.start(.unary,
metadata:metadata,
message:requestData)
{(callResult) in
if let responseData = callResult.resultData,
let response = try? EchoResponse(serializedData:responseData) {
completion(response, callResult)
} else {
completion(nil, callResult)
}
}
return self
}
/// Cancel the call.
internal func cancel() {
call.cancel()
}
}
/// Expand (Server Streaming)
internal final class EchoExpandCall {
private var call : Call
/// Create a call.
fileprivate init(_ channel: Channel) {
self.call = channel.makeCall("/Echo/Expand")
}
/// Call this once with the message to send. Nonblocking.
fileprivate func start(request: EchoRequest,
metadata: Metadata,
completion: @escaping (CallResult) -> ())
throws -> EchoExpandCall {
let requestData = try request.serializedData()
try call.start(.serverStreaming,
metadata:metadata,
message:requestData,
completion:completion)
return self
}
/// Call this to wait for a result. Blocking.
internal func receive() throws -> EchoResponse {
var returnError : EchoClientError?
var returnResponse : EchoResponse!
let sem = DispatchSemaphore(value: 0)
do {
try receive() {response, error in
returnResponse = response
returnError = error
sem.signal()
}
_ = sem.wait(timeout: DispatchTime.distantFuture)
}
if let returnError = returnError {
throw returnError
}
return returnResponse
}
/// Call this to wait for a result. Nonblocking.
internal func receive(completion:@escaping (EchoResponse?, EchoClientError?)->()) throws {
do {
try call.receiveMessage() {(responseData) in
if let responseData = responseData {
if let response = try? EchoResponse(serializedData:responseData) {
completion(response, nil)
} else {
completion(nil, EchoClientError.invalidMessageReceived)
}
} else {
completion(nil, EchoClientError.endOfStream)
}
}
}
}
/// Cancel the call.
internal func cancel() {
call.cancel()
}
}
/// Collect (Client Streaming)
internal final class EchoCollectCall {
private var call : Call
/// Create a call.
fileprivate init(_ channel: Channel) {
self.call = channel.makeCall("/Echo/Collect")
}
/// Call this to start a call. Nonblocking.
fileprivate func start(metadata:Metadata, completion:@escaping (CallResult)->())
throws -> EchoCollectCall {
try self.call.start(.clientStreaming, metadata:metadata, completion:completion)
return self
}
/// Call this to send each message in the request stream. Nonblocking.
internal func send(_ message:EchoRequest, errorHandler:@escaping (Error)->()) throws {
let messageData = try message.serializedData()
try call.sendMessage(data:messageData, errorHandler:errorHandler)
}
/// Call this to close the connection and wait for a response. Blocking.
internal func closeAndReceive() throws -> EchoResponse {
var returnError : EchoClientError?
var returnResponse : EchoResponse!
let sem = DispatchSemaphore(value: 0)
do {
try closeAndReceive() {response, error in
returnResponse = response
returnError = error
sem.signal()
}
_ = sem.wait(timeout: DispatchTime.distantFuture)
} catch (let error) {
throw error
}
if let returnError = returnError {
throw returnError
}
return returnResponse
}
/// Call this to close the connection and wait for a response. Nonblocking.
internal func closeAndReceive(completion:@escaping (EchoResponse?, EchoClientError?)->())
throws {
do {
try call.receiveMessage() {(responseData) in
if let responseData = responseData,
let response = try? EchoResponse(serializedData:responseData) {
completion(response, nil)
} else {
completion(nil, EchoClientError.invalidMessageReceived)
}
}
try call.close(completion:{})
} catch (let error) {
throw error
}
}
/// Cancel the call.
internal func cancel() {
call.cancel()
}
}
/// Update (Bidirectional Streaming)
internal final class EchoUpdateCall {
private var call : Call
/// Create a call.
fileprivate init(_ channel: Channel) {
self.call = channel.makeCall("/Echo/Update")
}
/// Call this to start a call. Nonblocking.
fileprivate func start(metadata:Metadata, completion:@escaping (CallResult)->())
throws -> EchoUpdateCall {
try self.call.start(.bidiStreaming, metadata:metadata, completion:completion)
return self
}
/// Call this to wait for a result. Blocking.
internal func receive() throws -> EchoResponse {
var returnError : EchoClientError?
var returnMessage : EchoResponse!
let sem = DispatchSemaphore(value: 0)
do {
try receive() {response, error in
returnMessage = response
returnError = error
sem.signal()
}
_ = sem.wait(timeout: DispatchTime.distantFuture)
}
if let returnError = returnError {
throw returnError
}
return returnMessage
}
/// Call this to wait for a result. Nonblocking.
internal func receive(completion:@escaping (EchoResponse?, EchoClientError?)->()) throws {
do {
try call.receiveMessage() {(data) in
if let data = data {
if let returnMessage = try? EchoResponse(serializedData:data) {
completion(returnMessage, nil)
} else {
completion(nil, EchoClientError.invalidMessageReceived)
}
} else {
completion(nil, EchoClientError.endOfStream)
}
}
}
}
/// Call this to send each message in the request stream.
internal func send(_ message:EchoRequest, errorHandler:@escaping (Error)->()) throws {
let messageData = try message.serializedData()
try call.sendMessage(data:messageData, errorHandler:errorHandler)
}
/// Call this to close the sending connection. Blocking.
internal func closeSend() throws {
let sem = DispatchSemaphore(value: 0)
try closeSend() {
sem.signal()
}
_ = sem.wait(timeout: DispatchTime.distantFuture)
}
/// Call this to close the sending connection. Nonblocking.
internal func closeSend(completion:@escaping ()->()) throws {
try call.close() {
completion()
}
}
/// Cancel the call.
internal func cancel() {
call.cancel()
}
}
/// Call methods of this class to make API calls.
internal final class EchoService {
public private(set) var channel: Channel
/// This metadata will be sent with all requests.
internal var metadata : Metadata
/// This property allows the service host name to be overridden.
/// For example, it can be used to make calls to "localhost:8080"
/// appear to be to "example.com".
internal var host : String {
get {
return self.channel.host
}
set {
self.channel.host = newValue
}
}
/// This property allows the service timeout to be overridden.
internal var timeout : TimeInterval {
get {
return self.channel.timeout
}
set {
self.channel.timeout = newValue
}
}
/// Create a client.
internal init(address: String, secure: Bool = true) {
gRPC.initialize()
channel = Channel(address:address, secure:secure)
metadata = Metadata()
}
/// Create a client that makes secure connections with a custom certificate and (optional) hostname.
internal init(address: String, certificates: String, host: String?) {
gRPC.initialize()
channel = Channel(address:address, certificates:certificates, host:host)
metadata = Metadata()
}
/// Synchronous. Unary.
internal func get(_ request: EchoRequest)
throws
-> EchoResponse {
return try EchoGetCall(channel).run(request:request, metadata:metadata)
}
/// Asynchronous. Unary.
internal func get(_ request: EchoRequest,
completion: @escaping (EchoResponse?, CallResult)->())
throws
-> EchoGetCall {
return try EchoGetCall(channel).start(request:request,
metadata:metadata,
completion:completion)
}
/// Asynchronous. Server-streaming.
/// Send the initial message.
/// Use methods on the returned object to get streamed responses.
internal func expand(_ request: EchoRequest, completion: @escaping (CallResult)->())
throws
-> EchoExpandCall {
return try EchoExpandCall(channel).start(request:request, metadata:metadata, completion:completion)
}
/// Asynchronous. Client-streaming.
/// Use methods on the returned object to stream messages and
/// to close the connection and wait for a final response.
internal func collect(completion: @escaping (CallResult)->())
throws
-> EchoCollectCall {
return try EchoCollectCall(channel).start(metadata:metadata, completion:completion)
}
/// Asynchronous. Bidirectional-streaming.
/// Use methods on the returned object to stream messages,
/// to wait for replies, and to close the connection.
internal func update(completion: @escaping (CallResult)->())
throws
-> EchoUpdateCall {
return try EchoUpdateCall(channel).start(metadata:metadata, completion:completion)
}
}
/// Type for errors thrown from generated server code.
internal enum EchoServerError : Error {
case endOfStream
}
/// To build a server, implement a class that conforms to this protocol.
internal protocol EchoProvider {
func get(request : EchoRequest, session : EchoGetSession) throws -> EchoResponse
func expand(request : EchoRequest, session : EchoExpandSession) throws
func collect(session : EchoCollectSession) throws
func update(session : EchoUpdateSession) throws
}
/// Common properties available in each service session.
internal class EchoSession {
fileprivate var handler : gRPC.Handler
internal var requestMetadata : Metadata { return handler.requestMetadata }
internal var statusCode : StatusCode = .ok
internal var statusMessage : String = "OK"
internal var initialMetadata : Metadata = Metadata()
internal var trailingMetadata : Metadata = Metadata()
fileprivate init(handler:gRPC.Handler) {
self.handler = handler
}
}
// Get (Unary)
internal final class EchoGetSession : EchoSession {
private var provider : EchoProvider
/// Create a session.
fileprivate init(handler:gRPC.Handler, provider: EchoProvider) {
self.provider = provider
super.init(handler:handler)
}
/// Run the session. Internal.
fileprivate func run(queue:DispatchQueue) throws {
try handler.receiveMessage(initialMetadata:initialMetadata) {(requestData) in
if let requestData = requestData {
let requestMessage = try EchoRequest(serializedData:requestData)
let replyMessage = try self.provider.get(request:requestMessage, session: self)
try self.handler.sendResponse(message:replyMessage.serializedData(),
statusCode:self.statusCode,
statusMessage:self.statusMessage,
trailingMetadata:self.trailingMetadata)
}
}
}
}
// Expand (Server Streaming)
internal final class EchoExpandSession : EchoSession {
private var provider : EchoProvider
/// Create a session.
fileprivate init(handler:gRPC.Handler, provider: EchoProvider) {
self.provider = provider
super.init(handler:handler)
}
/// Send a message. Nonblocking.
internal func send(_ response: EchoResponse, completion: @escaping ()->()) throws {
try handler.sendResponse(message:response.serializedData()) {completion()}
}
/// Run the session. Internal.
fileprivate func run(queue:DispatchQueue) throws {
try self.handler.receiveMessage(initialMetadata:initialMetadata) {(requestData) in
if let requestData = requestData {
do {
let requestMessage = try EchoRequest(serializedData:requestData)
// to keep providers from blocking the server thread,
// we dispatch them to another queue.
queue.async {
do {
try self.provider.expand(request:requestMessage, session: self)
try self.handler.sendStatus(statusCode:self.statusCode,
statusMessage:self.statusMessage,
trailingMetadata:self.trailingMetadata,
completion:{})
} catch (let error) {
print("error: \(error)")
}
}
} catch (let error) {
print("error: \(error)")
}
}
}
}
}
// Collect (Client Streaming)
internal final class EchoCollectSession : EchoSession {
private var provider : EchoProvider
/// Create a session.
fileprivate init(handler:gRPC.Handler, provider: EchoProvider) {
self.provider = provider
super.init(handler:handler)
}
/// Receive a message. Blocks until a message is received or the client closes the connection.
internal func receive() throws -> EchoRequest {
let sem = DispatchSemaphore(value: 0)
var requestMessage : EchoRequest?
try self.handler.receiveMessage() {(requestData) in
if let requestData = requestData {
requestMessage = try? EchoRequest(serializedData:requestData)
}
sem.signal()
}
_ = sem.wait(timeout: DispatchTime.distantFuture)
if requestMessage == nil {
throw EchoServerError.endOfStream
}
return requestMessage!
}
/// Send a response and close the connection.
internal func sendAndClose(_ response: EchoResponse) throws {
try self.handler.sendResponse(message:response.serializedData(),
statusCode:self.statusCode,
statusMessage:self.statusMessage,
trailingMetadata:self.trailingMetadata)
}
/// Run the session. Internal.
fileprivate func run(queue:DispatchQueue) throws {
try self.handler.sendMetadata(initialMetadata:initialMetadata) {
queue.async {
do {
try self.provider.collect(session:self)
} catch (let error) {
print("error \(error)")
}
}
}
}
}
// Update (Bidirectional Streaming)
internal final class EchoUpdateSession : EchoSession {
private var provider : EchoProvider
/// Create a session.
fileprivate init(handler:gRPC.Handler, provider: EchoProvider) {
self.provider = provider
super.init(handler:handler)
}
/// Receive a message. Blocks until a message is received or the client closes the connection.
internal func receive() throws -> EchoRequest {
let sem = DispatchSemaphore(value: 0)
var requestMessage : EchoRequest?
try self.handler.receiveMessage() {(requestData) in
if let requestData = requestData {
do {
requestMessage = try EchoRequest(serializedData:requestData)
} catch (let error) {
print("error \(error)")
}
}
sem.signal()
}
_ = sem.wait(timeout: DispatchTime.distantFuture)
if let requestMessage = requestMessage {
return requestMessage
} else {
throw EchoServerError.endOfStream
}
}
/// Send a message. Nonblocking.
internal func send(_ response: EchoResponse, completion: @escaping ()->()) throws {
try handler.sendResponse(message:response.serializedData()) {completion()}
}
/// Close a connection. Blocks until the connection is closed.
internal func close() throws {
let sem = DispatchSemaphore(value: 0)
try self.handler.sendStatus(statusCode:self.statusCode,
statusMessage:self.statusMessage,
trailingMetadata:self.trailingMetadata) {
sem.signal()
}
_ = sem.wait(timeout: DispatchTime.distantFuture)
}
/// Run the session. Internal.
fileprivate func run(queue:DispatchQueue) throws {
try self.handler.sendMetadata(initialMetadata:initialMetadata) {
queue.async {
do {
try self.provider.update(session:self)
} catch (let error) {
print("error \(error)")
}
}
}
}
}
/// Main server for generated service
internal final class EchoServer {
private var address: String
private var server: gRPC.Server
private var provider: EchoProvider?
/// Create a server that accepts insecure connections.
internal init(address:String,
provider:EchoProvider) {
gRPC.initialize()
self.address = address
self.provider = provider
self.server = gRPC.Server(address:address)
}
/// Create a server that accepts secure connections.
internal init?(address:String,
certificateURL:URL,
keyURL:URL,
provider:EchoProvider) {
gRPC.initialize()
self.address = address
self.provider = provider
guard
let certificate = try? String(contentsOf: certificateURL, encoding: .utf8),
let key = try? String(contentsOf: keyURL, encoding: .utf8)
else {
return nil
}
self.server = gRPC.Server(address:address, key:key, certs:certificate)
}
/// Start the server.
internal func start(queue:DispatchQueue = DispatchQueue.global()) {
guard let provider = self.provider else {
fatalError() // the server requires a provider
}
server.run {(handler) in
print("Server received request to " + handler.host
+ " calling " + handler.method
+ " from " + handler.caller
+ " with " + String(describing:handler.requestMetadata) )
do {
switch handler.method {
case "/Echo/Get":
try EchoGetSession(handler:handler, provider:provider).run(queue:queue)
case "/Echo/Expand":
try EchoExpandSession(handler:handler, provider:provider).run(queue:queue)
case "/Echo/Collect":
try EchoCollectSession(handler:handler, provider:provider).run(queue:queue)
case "/Echo/Update":
try EchoUpdateSession(handler:handler, provider:provider).run(queue:queue)
default:
// handle unknown requests
try handler.receiveMessage(initialMetadata:Metadata()) {(requestData) in
try handler.sendResponse(statusCode:.unimplemented,
statusMessage:"unknown method " + handler.method,
trailingMetadata:Metadata())
}
}
} catch (let error) {
print("Server error: \(error)")
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment