Browse Source

Begin generating code using a snapshot of Kyle Fuller's Stencil templating package.

https://github.com/kylef/Stencil
Tim Burks 9 years ago
parent
commit
6f8eac1d8a

+ 12 - 0
Plugin/Makefile

@@ -0,0 +1,12 @@
+test:
+	rm -f echo.client.pb.swift echo.server.pb.swift
+	swift build
+	cp .build/debug/protoc-gen-swiftgrpc .
+	protoc echo.proto --swiftgrpc_out=.
+
+clean :
+	rm -f protoc-gen-swiftgrpc 
+	rm -f swiftgrpc.log
+	rm -f echo.client.pb.swift echo.server.pb.swift
+	rm -rf Packages
+	rm -rf .build

+ 2 - 1
Plugin/Package.swift

@@ -17,6 +17,7 @@ import PackageDescription
 let package = Package(
     name: "protoc-gen-swiftgrpc",
     dependencies: [
-        .Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,24))
+        .Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,24)),
+        .Package(url: "https://github.com/timburks/Stencil.git", Version(9,9,9)) // temporary fork
     ]
 )

+ 29 - 2
Plugin/Sources/main.swift

@@ -12,11 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import Stencil
 import Foundation
 import SwiftProtobuf
 import PluginLibrary
 
 func main() throws {
+
+    let fileSystemLoader = Stencil.FileSystemLoader(paths: ["templates/"])
+    let templateEnvironment = Environment(loader: fileSystemLoader)
+	
   var response = Google_Protobuf_Compiler_CodeGeneratorResponse()
 
   var log = ""
@@ -25,6 +30,10 @@ func main() throws {
   let request = try Google_Protobuf_Compiler_CodeGeneratorRequest(protobuf: rawRequest)
 
   for protoFile in request.protoFile {
+	
+	
+  	if let package = protoFile.package { 
+		  
     log += "File \(protoFile.name!)\n"
     for service in protoFile.service {
       log += "Service \(service.name!)\n"
@@ -37,13 +46,31 @@ func main() throws {
       }
       log += " Options \(service.options)\n"
     }
+	
+    let context = ["protoFile": protoFile]
+    let clientcode = try templateEnvironment.renderTemplate(name: "PACKAGE.client.pb.swift", context: context)
+    let servercode = try templateEnvironment.renderTemplate(name: "PACKAGE.server.pb.swift", context: context)
+
+    var clientfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
+    clientfile.name = package + ".client.pb.swift"
+    clientfile.content = clientcode
+    response.file.append(clientfile)
+
+    var serverfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
+    serverfile.name = package + ".server.pb.swift"
+    serverfile.content = servercode
+    response.file.append(serverfile)
+    } else {
+    	print("ERROR: no package")
+    }
   }
-  
+  log += "\n\n\n\(request)"
+
   var logfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
   logfile.name = "swiftgrpc.log"
   logfile.content = log
   response.file.append(logfile)
-
+  
   let serializedResponse = try response.serializeProtobuf()
   Stdout.write(bytes: serializedResponse)
 }

+ 6 - 0
Plugin/TRYME.sh

@@ -0,0 +1,6 @@
+#!/bin/sh
+
+rm echo.client.pb.swift echo.server.pb.swift
+swift build
+cp .build/debug/protoc-gen-swiftgrpc .
+protoc echo.proto --swiftgrpc_out=.

+ 31 - 0
Plugin/echo.proto

@@ -0,0 +1,31 @@
+// 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.
+
+syntax = "proto3";
+package echo;
+
+service Echo {
+  rpc Get(EchoRequest) returns (EchoResponse) {}
+  rpc Update(stream EchoRequest) returns (stream EchoResponse) {}
+  rpc Collect(stream EchoRequest) returns (EchoResponse) {}
+  rpc Expand(EchoRequest) returns (stream EchoResponse) {}  
+}
+
+message EchoRequest {
+  string text = 1;
+}
+
+message EchoResponse {
+  string text = 1;
+}

+ 341 - 0
Plugin/templates/PACKAGE.client.pb.swift

@@ -0,0 +1,341 @@
+/*
+ * DO NOT EDIT.
+ *
+ * Generated by the protocol buffer compiler.
+ * Source: {{ protoFile.name }}
+ *
+ */
+
+/*
+ *
+ * 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
+
+public enum Echo_EchoClientError : Error {
+  case endOfStream
+  case invalidMessageReceived
+  case error(c: CallResult)
+}
+
+//
+// Unary GET
+//
+public class Echo_EchoGetCall {
+  var call : Call
+
+  fileprivate init(_ channel: Channel) {
+    self.call = channel.makeCall("/echo.Echo/Get")
+  }
+
+  fileprivate func run(request: Echo_EchoRequest,
+                       metadata: Metadata) throws -> Echo_EchoResponse {
+    let done = NSCondition()
+    var callResult : CallResult!
+    var responseMessage : Echo_EchoResponse?
+    let requestMessageData = try! request.serializeProtobuf()
+    try! call.perform(message: requestMessageData,
+                      metadata: metadata)
+    {(_callResult) in
+      callResult = _callResult
+      if let messageData = callResult.resultData {
+        responseMessage = try? Echo_EchoResponse(protobuf:messageData)
+      }
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+    if let responseMessage = responseMessage {
+      return responseMessage
+    } else {
+      throw Echo_EchoClientError.error(c: callResult)
+    }
+  }
+}
+
+//
+// Server-streaming EXPAND
+//
+public class Echo_EchoExpandCall {
+  var call : Call
+
+  fileprivate init(_ channel: Channel) {
+    self.call = channel.makeCall("/echo.Echo/Expand")
+  }
+
+  // Call this once with the message to send.
+  fileprivate func run(request: Echo_EchoRequest, metadata: Metadata) throws -> Echo_EchoExpandCall {
+    let requestMessageData = try! request.serializeProtobuf()
+    try! call.startServerStreaming(message: requestMessageData,
+                                   metadata: metadata,
+                                   completion:{(CallResult) in })
+    return self
+  }
+
+  // Call this to wait for a result. Blocks.
+  public func Receive() throws -> Echo_EchoResponse {
+    var returnError : Echo_EchoClientError?
+    var returnMessage : Echo_EchoResponse!
+    let done = NSCondition()
+    do {
+      try call.receiveMessage() {(data) in
+        if let data = data {
+          returnMessage = try? Echo_EchoResponse(protobuf:data)
+          if returnMessage == nil {
+            returnError = Echo_EchoClientError.invalidMessageReceived
+          }
+        } else {
+          returnError = Echo_EchoClientError.endOfStream
+        }
+        done.lock()
+        done.signal()
+        done.unlock()
+      }
+      done.lock()
+      done.wait()
+      done.unlock()
+    }
+    if let returnError = returnError {
+      throw returnError
+    }
+    return returnMessage
+  }
+}
+
+//
+// Client-streaming COLLECT
+//
+public class Echo_EchoCollectCall {
+  var call : Call
+
+  fileprivate init(_ channel: Channel) {
+    self.call = channel.makeCall("/echo.Echo/Collect")
+  }
+
+  // Call this to start a call.
+  fileprivate func run(metadata:Metadata) throws -> Echo_EchoCollectCall {
+    try self.call.start(metadata: metadata, completion:{})
+    return self
+  }
+
+  // Call this to send each message in the request stream.
+  public func Send(_ message: Echo_EchoRequest) {
+    let messageData = try! message.serializeProtobuf()
+    _ = call.sendMessage(data:messageData)
+  }
+
+  // Call this to close the connection and wait for a response. Blocks.
+  public func CloseAndReceive() throws -> Echo_EchoResponse {
+    var returnError : Echo_EchoClientError?
+    var returnMessage : Echo_EchoResponse!
+    let done = NSCondition()
+
+    do {
+      try self.receiveMessage() {(responseMessage) in
+        if let responseMessage = responseMessage {
+          returnMessage = responseMessage
+        } else {
+          returnError = Echo_EchoClientError.invalidMessageReceived
+        }
+        done.lock()
+        done.signal()
+        done.unlock()
+      }
+      try call.close(completion:{
+        print("closed")
+      })
+      done.lock()
+      done.wait()
+      done.unlock()
+    } catch (let error) {
+      print("ERROR B: \(error)")
+    }
+
+    if let returnError = returnError {
+      throw returnError
+    }
+    return returnMessage
+  }
+
+  // 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.
+  fileprivate func receiveMessage(callback:@escaping (Echo_EchoResponse?) throws -> Void)
+    throws {
+      try call.receiveMessage() {(data) in
+        guard let data = data else {
+          try callback(nil)
+          return
+        }
+        guard
+          let responseMessage = try? Echo_EchoResponse(protobuf:data)
+          else {
+            return
+        }
+        try callback(responseMessage)
+      }
+  }
+
+}
+
+//
+// Bidirectional-streaming UPDATE
+//
+public class Echo_EchoUpdateCall {
+  var call : Call
+
+  fileprivate init(_ channel: Channel) {
+    self.call = channel.makeCall("/echo.Echo/Update")
+  }
+
+  fileprivate func run(metadata:Metadata) throws -> Echo_EchoUpdateCall {
+    try self.call.start(metadata: metadata, completion:{})
+    return self
+  }
+
+  fileprivate 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)
+      }
+    }
+  }
+
+  public func Receive() throws -> Echo_EchoResponse {
+    var returnError : Echo_EchoClientError?
+    var returnMessage : Echo_EchoResponse!
+    let done = NSCondition()
+    do {
+      try call.receiveMessage() {(data) in
+        if let data = data {
+          returnMessage = try? Echo_EchoResponse(protobuf:data)
+          if returnMessage == nil {
+            returnError = Echo_EchoClientError.invalidMessageReceived
+          }
+        } else {
+          returnError = Echo_EchoClientError.endOfStream
+        }
+        done.lock()
+        done.signal()
+        done.unlock()
+      }
+      done.lock()
+      done.wait()
+      done.unlock()
+    }
+    if let returnError = returnError {
+      throw returnError
+    }
+    return returnMessage
+  }
+
+  public func Send(_ message:Echo_EchoRequest) {
+    let messageData = try! message.serializeProtobuf()
+    _ = call.sendMessage(data:messageData)
+  }
+
+  public func CloseSend() {
+    let done = NSCondition()
+    try! call.close() {
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+  }
+}
+
+// Call methods of this class to make API calls.
+public class Echo_EchoService {
+  private var channel: Channel
+
+  public var metadata : Metadata
+
+  public var host : String {
+    get {
+      return self.channel.host
+    }
+    set {
+      self.channel.host = newValue
+    }
+  }
+
+  public init(address: String) {
+    gRPC.initialize()
+    channel = Channel(address:address)
+    metadata = Metadata()
+  }
+
+  public init(address: String, certificates: String?, host: String?) {
+    gRPC.initialize()
+    channel = Channel(address:address, certificates:certificates, host:host)
+    metadata = Metadata()
+  }
+
+  // Synchronous. Unary.
+  public func get(_ request: Echo_EchoRequest) throws -> Echo_EchoResponse {
+    return try Echo_EchoGetCall(channel).run(request:request, metadata:metadata)
+  }
+
+  // Asynchronous. Server-streaming.
+  // Send the initial message.
+  // Use methods on the returned object to get streamed responses.
+  public func expand(_ request: Echo_EchoRequest) throws -> Echo_EchoExpandCall {
+    return try Echo_EchoExpandCall(channel).run(request:request, metadata:metadata)
+  }
+
+  // Asynchronous. Client-streaming.
+  // Use methods on the returned object to stream messages and
+  // to close the connection and wait for a final response.
+  public func collect() throws -> Echo_EchoCollectCall {
+    return try Echo_EchoCollectCall(channel).run(metadata:metadata)
+  }
+
+  // Asynchronous. Bidirectional-streaming.
+  // Use methods on the returned object to stream messages,
+  // to wait for replies, and to close the connection.
+  public func update() throws -> Echo_EchoUpdateCall {
+    return try Echo_EchoUpdateCall(channel).run(metadata:metadata)
+  }
+}

+ 289 - 0
Plugin/templates/PACKAGE.server.pb.swift

@@ -0,0 +1,289 @@
+/*
+ * DO NOT EDIT.
+ *
+ * Generated by the protocol buffer compiler.
+ * Source: {{ protoFile.name }}
+ *
+ */
+
+/*
+ *
+ * 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
+
+public enum Echo_EchoServerError : Error {
+  case endOfStream
+}
+
+public protocol Echo_EchoProvider {
+  func get(request : Echo_EchoRequest) throws -> Echo_EchoResponse
+  func collect(session : Echo_EchoCollectSession) throws
+  func expand(request : Echo_EchoRequest, session : Echo_EchoExpandSession) throws
+  func update(session : Echo_EchoUpdateSession) throws
+}
+
+// unary
+public class Echo_EchoGetSession {
+  var handler : gRPC.Handler
+  var provider : Echo_EchoProvider
+
+  fileprivate init(handler:gRPC.Handler, provider: Echo_EchoProvider) {
+    self.handler = handler
+    self.provider = provider
+  }
+
+  fileprivate func run(queue:DispatchQueue) {
+    do {
+      try handler.receiveMessage(initialMetadata:Metadata()) {(requestData) in
+        if let requestData = requestData {
+          let requestMessage = try! Echo_EchoRequest(protobuf:requestData)
+          let replyMessage = try! self.provider.get(request:requestMessage)
+          try self.handler.sendResponse(message:replyMessage.serializeProtobuf(),
+                                        statusCode: 0,
+                                        statusMessage: "OK",
+                                        trailingMetadata:Metadata())
+
+        }
+      }
+    } catch (let callError) {
+      print("grpc error: \(callError)")
+    }
+  }
+}
+
+// server streaming
+public class Echo_EchoExpandSession {
+  var handler : gRPC.Handler
+  var provider : Echo_EchoProvider
+
+  fileprivate init(handler:gRPC.Handler, provider: Echo_EchoProvider) {
+    self.handler = handler
+    self.provider = provider
+  }
+
+  public func Send(_ response: Echo_EchoResponse) throws {
+    try! handler.sendResponse(message:response.serializeProtobuf()) {}
+  }
+
+  fileprivate func run(queue:DispatchQueue) {
+    do {
+      try self.handler.receiveMessage(initialMetadata:Metadata()) {(requestData) in
+        if let requestData = requestData {
+          let requestMessage = try! Echo_EchoRequest(protobuf:requestData)
+          // to keep providers from blocking the server thread,
+          // we dispatch them to another queue.
+          queue.async {
+            try! self.provider.expand(request:requestMessage, session: self)
+            try! self.handler.sendStatus(statusCode:0,
+                                         statusMessage:"OK",
+                                         trailingMetadata:Metadata(),
+                                         completion:{})
+          }
+        }
+      }
+    } catch (let callError) {
+      print("grpc error: \(callError)")
+    }
+  }
+}
+
+// client streaming
+public class Echo_EchoCollectSession {
+  var handler : gRPC.Handler
+  var provider : Echo_EchoProvider
+
+  fileprivate init(handler:gRPC.Handler, provider: Echo_EchoProvider) {
+    self.handler = handler
+    self.provider = provider
+  }
+
+  public func Receive() throws -> Echo_EchoRequest {
+    let done = NSCondition()
+    var requestMessage : Echo_EchoRequest?
+    try self.handler.receiveMessage() {(requestData) in
+      if let requestData = requestData {
+        requestMessage = try! Echo_EchoRequest(protobuf:requestData)
+      }
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+    if requestMessage == nil {
+      throw Echo_EchoServerError.endOfStream
+    }
+    return requestMessage!
+  }
+
+  public func SendAndClose(_ response: Echo_EchoResponse) throws {
+    try! self.handler.sendResponse(message:response.serializeProtobuf(),
+                                   statusCode: 0,
+                                   statusMessage: "OK",
+                                   trailingMetadata: Metadata())
+  }
+
+  fileprivate func run(queue:DispatchQueue) {
+    do {
+      print("EchoCollectSession run")
+      try self.handler.sendMetadata(initialMetadata:Metadata()) {
+        queue.async {
+          try! self.provider.collect(session:self)
+        }
+      }
+    } catch (let callError) {
+      print("grpc error: \(callError)")
+    }
+  }
+}
+
+// fully streaming
+public class Echo_EchoUpdateSession {
+  var handler : gRPC.Handler
+  var provider : Echo_EchoProvider
+
+  fileprivate init(handler:gRPC.Handler, provider: Echo_EchoProvider) {
+    self.handler = handler
+    self.provider = provider
+  }
+
+  public func Receive() throws -> Echo_EchoRequest {
+    let done = NSCondition()
+    var requestMessage : Echo_EchoRequest?
+    try self.handler.receiveMessage() {(requestData) in
+      if let requestData = requestData {
+        requestMessage = try! Echo_EchoRequest(protobuf:requestData)
+      }
+      done.lock()
+      done.signal()
+      done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+    if requestMessage == nil {
+      throw Echo_EchoServerError.endOfStream
+    }
+    return requestMessage!
+  }
+
+  public func Send(_ response: Echo_EchoResponse) throws {
+    try handler.sendResponse(message:response.serializeProtobuf()) {}
+  }
+
+  public func Close() {
+    let done = NSCondition()
+    try! self.handler.sendStatus(statusCode: 0,
+                                 statusMessage: "OK",
+                                 trailingMetadata: Metadata()) {
+                                  done.lock()
+                                  done.signal()
+                                  done.unlock()
+    }
+    done.lock()
+    done.wait()
+    done.unlock()
+  }
+
+  fileprivate func run(queue:DispatchQueue) {
+    do {
+      try self.handler.sendMetadata(initialMetadata:Metadata()) {
+        queue.async {
+          try! self.provider.update(session:self)
+        }
+      }
+    } catch (let callError) {
+      print("grpc error: \(callError)")
+    }
+  }
+}
+
+//
+// main server for generated service
+//
+public class Echo_EchoServer {
+  private var address: String
+  private var server: gRPC.Server
+  public var provider: Echo_EchoProvider?
+
+  public init(address:String,
+              provider:Echo_EchoProvider) {
+    gRPC.initialize()
+    self.address = address
+    self.provider = provider
+    self.server = gRPC.Server(address:address)
+  }
+
+  public init?(address:String,
+               certificateURL:URL,
+               keyURL:URL,
+               provider:Echo_EchoProvider) {
+    gRPC.initialize()
+    self.address = address
+    self.provider = provider
+    guard
+      let certificate = try? String(contentsOf: certificateURL),
+      let key = try? String(contentsOf: keyURL)
+      else {
+        return nil
+    }
+    self.server = gRPC.Server(address:address, key:key, certs:certificate)
+  }
+
+  public func start(queue:DispatchQueue = DispatchQueue.global()) {
+    guard let provider = self.provider else {
+      assert(false) // the server requires a provider
+    }
+    server.run {(handler) in
+      print("Server received request to " + handler.host
+        + " calling " + handler.method
+        + " from " + handler.caller)
+
+      switch handler.method {
+      case "/echo.Echo/Get":
+        Echo_EchoGetSession(handler:handler, provider:provider).run(queue:queue)
+      case "/echo.Echo/Expand":
+        Echo_EchoExpandSession(handler:handler, provider:provider).run(queue:queue)
+      case "/echo.Echo/Collect":
+        Echo_EchoCollectSession(handler:handler, provider:provider).run(queue:queue)
+      case "/echo.Echo/Update":
+        Echo_EchoUpdateSession(handler:handler, provider:provider).run(queue:queue)
+      default:
+        break // handle unknown requests
+      }
+    }
+  }
+}
+