mirror of https://github.com/grpc/grpc.git
commit
c230a7451d
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