Merge pull request #411 from jtattermusch/csharp_server
Improved C# grpc server implementation to be able to register call handlerschanges/72/217572/1
commit
3d6fa14f1a
13 changed files with 364 additions and 59 deletions
@ -0,0 +1,31 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// For serializing and deserializing messages. |
||||||
|
/// </summary> |
||||||
|
public interface IMarshaller<T> |
||||||
|
{ |
||||||
|
byte[] Serialize(T value); |
||||||
|
|
||||||
|
T Deserialize(byte[] payload); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// UTF-8 Marshalling for string. Useful for testing. |
||||||
|
/// </summary> |
||||||
|
internal class StringMarshaller : IMarshaller<string> { |
||||||
|
|
||||||
|
public byte[] Serialize(string value) |
||||||
|
{ |
||||||
|
return System.Text.Encoding.UTF8.GetBytes(value); |
||||||
|
} |
||||||
|
|
||||||
|
public string Deserialize(byte[] payload) |
||||||
|
{ |
||||||
|
return System.Text.Encoding.UTF8.GetString(payload); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,38 @@ |
|||||||
|
using System; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Internal |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Observer that writes all arriving messages to a call abstraction (in blocking fashion) |
||||||
|
/// and then halfcloses the call. Used for server-side call handling. |
||||||
|
/// </summary> |
||||||
|
internal class ServerWritingObserver<TWrite, TRead> : IObserver<TWrite> |
||||||
|
{ |
||||||
|
readonly AsyncCall<TWrite, TRead> call; |
||||||
|
|
||||||
|
public ServerWritingObserver(AsyncCall<TWrite, TRead> call) |
||||||
|
{ |
||||||
|
this.call = call; |
||||||
|
} |
||||||
|
|
||||||
|
public void OnCompleted() |
||||||
|
{ |
||||||
|
// TODO: how bad is the Wait here? |
||||||
|
call.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait(); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnError(Exception error) |
||||||
|
{ |
||||||
|
// TODO: handle this... |
||||||
|
throw new InvalidOperationException("This should never be called."); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnNext(TWrite value) |
||||||
|
{ |
||||||
|
// TODO: how bad is the Wait here? |
||||||
|
call.WriteAsync(value).Wait(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,64 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
public enum MethodType |
||||||
|
{ |
||||||
|
Unary, |
||||||
|
ClientStreaming, |
||||||
|
ServerStreaming, |
||||||
|
DuplexStreaming |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// A description of a service method. |
||||||
|
/// </summary> |
||||||
|
public class Method<TRequest, TResponse> |
||||||
|
{ |
||||||
|
readonly MethodType type; |
||||||
|
readonly string name; |
||||||
|
readonly IMarshaller<TRequest> requestMarshaller; |
||||||
|
readonly IMarshaller<TResponse> responseMarshaller; |
||||||
|
|
||||||
|
public Method(MethodType type, string name, IMarshaller<TRequest> requestMarshaller, IMarshaller<TResponse> responseMarshaller) |
||||||
|
{ |
||||||
|
this.type = type; |
||||||
|
this.name = name; |
||||||
|
this.requestMarshaller = requestMarshaller; |
||||||
|
this.responseMarshaller = responseMarshaller; |
||||||
|
} |
||||||
|
|
||||||
|
public MethodType Type |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.type; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public string Name |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IMarshaller<TRequest> RequestMarshaller |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.requestMarshaller; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IMarshaller<TResponse> ResponseMarshaller |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return this.responseMarshaller; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,93 @@ |
|||||||
|
using System; |
||||||
|
using Google.GRPC.Core.Internal; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
internal interface IServerCallHandler |
||||||
|
{ |
||||||
|
void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq); |
||||||
|
} |
||||||
|
|
||||||
|
internal class UnaryRequestServerCallHandler<TRequest, TResponse> : IServerCallHandler |
||||||
|
{ |
||||||
|
readonly Method<TRequest, TResponse> method; |
||||||
|
readonly UnaryRequestServerMethod<TRequest, TResponse> handler; |
||||||
|
|
||||||
|
public UnaryRequestServerCallHandler(Method<TRequest, TResponse> method, UnaryRequestServerMethod<TRequest, TResponse> handler) |
||||||
|
{ |
||||||
|
this.method = method; |
||||||
|
this.handler = handler; |
||||||
|
} |
||||||
|
|
||||||
|
public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq) |
||||||
|
{ |
||||||
|
var asyncCall = new AsyncCall<TResponse, TRequest>( |
||||||
|
(msg) => method.ResponseMarshaller.Serialize(msg), |
||||||
|
(payload) => method.RequestMarshaller.Deserialize(payload)); |
||||||
|
|
||||||
|
asyncCall.InitializeServer(call); |
||||||
|
asyncCall.Accept(cq); |
||||||
|
|
||||||
|
var request = asyncCall.ReadAsync().Result; |
||||||
|
|
||||||
|
var responseObserver = new ServerWritingObserver<TResponse, TRequest>(asyncCall); |
||||||
|
handler(request, responseObserver); |
||||||
|
|
||||||
|
asyncCall.Halfclosed.Wait(); |
||||||
|
// TODO: wait until writing is finished |
||||||
|
|
||||||
|
asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait(); |
||||||
|
asyncCall.Finished.Wait(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class StreamingRequestServerCallHandler<TRequest, TResponse> : IServerCallHandler |
||||||
|
{ |
||||||
|
readonly Method<TRequest, TResponse> method; |
||||||
|
readonly StreamingRequestServerMethod<TRequest, TResponse> handler; |
||||||
|
|
||||||
|
public StreamingRequestServerCallHandler(Method<TRequest, TResponse> method, StreamingRequestServerMethod<TRequest, TResponse> handler) |
||||||
|
{ |
||||||
|
this.method = method; |
||||||
|
this.handler = handler; |
||||||
|
} |
||||||
|
|
||||||
|
public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq) |
||||||
|
{ |
||||||
|
var asyncCall = new AsyncCall<TResponse, TRequest>( |
||||||
|
(msg) => method.ResponseMarshaller.Serialize(msg), |
||||||
|
(payload) => method.RequestMarshaller.Deserialize(payload)); |
||||||
|
|
||||||
|
asyncCall.InitializeServer(call); |
||||||
|
asyncCall.Accept(cq); |
||||||
|
|
||||||
|
var responseObserver = new ServerWritingObserver<TResponse, TRequest>(asyncCall); |
||||||
|
var requestObserver = handler(responseObserver); |
||||||
|
|
||||||
|
// feed the requests |
||||||
|
asyncCall.StartReadingToStream(requestObserver); |
||||||
|
|
||||||
|
asyncCall.Halfclosed.Wait(); |
||||||
|
|
||||||
|
asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait(); |
||||||
|
asyncCall.Finished.Wait(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class NoSuchMethodCallHandler : IServerCallHandler |
||||||
|
{ |
||||||
|
public void StartCall(string methodName, CallSafeHandle call, CompletionQueueSafeHandle cq) |
||||||
|
{ |
||||||
|
// We don't care about the payload type here. |
||||||
|
AsyncCall<byte[], byte[]> asyncCall = new AsyncCall<byte[], byte[]>( |
||||||
|
(payload) => payload, (payload) => payload); |
||||||
|
|
||||||
|
asyncCall.InitializeServer(call); |
||||||
|
asyncCall.Accept(cq); |
||||||
|
asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_UNIMPLEMENTED, "No such method.")).Wait(); |
||||||
|
|
||||||
|
asyncCall.Finished.Wait(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,25 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core |
||||||
|
{ |
||||||
|
// TODO: perhaps add also serverSideStreaming and clientSideStreaming |
||||||
|
|
||||||
|
public delegate void UnaryRequestServerMethod<TRequest, TResponse> (TRequest request, IObserver<TResponse> responseObserver); |
||||||
|
|
||||||
|
public delegate IObserver<TRequest> StreamingRequestServerMethod<TRequest, TResponse> (IObserver<TResponse> responseObserver); |
||||||
|
|
||||||
|
internal static class ServerCalls { |
||||||
|
|
||||||
|
public static IServerCallHandler UnaryRequestCall<TRequest, TResponse>(Method<TRequest, TResponse> method, UnaryRequestServerMethod<TRequest, TResponse> handler) |
||||||
|
{ |
||||||
|
return new UnaryRequestServerCallHandler<TRequest, TResponse>(method, handler); |
||||||
|
} |
||||||
|
|
||||||
|
public static IServerCallHandler StreamingRequestCall<TRequest, TResponse>(Method<TRequest, TResponse> method, StreamingRequestServerMethod<TRequest, TResponse> handler) |
||||||
|
{ |
||||||
|
return new StreamingRequestServerCallHandler<TRequest, TResponse>(method, handler); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue