using Grpc.Core; using MessagePack; using System; using System.Threading; using System.Threading.Tasks; namespace MagicOnion.Client { public abstract class MagicOnionClientBase { protected string host; protected CallOptions option; protected CallInvoker callInvoker; protected MessagePackSerializerOptions serializerOptions; protected IClientFilter[] filters; static protected ResponseContext CreateResponseContext(RequestContext context, Method method) { var self = context.Client; var callResult = self.callInvoker.AsyncUnaryCall(method, self.host, context.CallOptions, context.RequestMutator(MagicOnionMarshallers.UnsafeNilBytes)); var response = new ResponseContext(callResult, self.serializerOptions); response.SetResponseMutator(context.ResponseMutator); return response; } static protected ResponseContext CreateResponseContext(RequestContext context, Method method) { var self = context.Client; var message = MessagePackSerializer.Serialize(((RequestContext)context).Request, self.serializerOptions); var callResult = self.callInvoker.AsyncUnaryCall(method, self.host, context.CallOptions, context.RequestMutator(message)); var response = new ResponseContext(callResult, self.serializerOptions); response.SetResponseMutator(context.ResponseMutator); return response; } } public abstract class MagicOnionClientBase : MagicOnionClientBase where T : IService { protected MagicOnionClientBase() { } protected MagicOnionClientBase(CallInvoker callInvoker, MessagePackSerializerOptions serializerOptions, IClientFilter[] filters) { this.callInvoker = callInvoker; this.serializerOptions = serializerOptions; this.filters = filters; } protected UnaryResult InvokeAsync(string path, TRequest request, Func requestMethod) { var future = InvokeAsyncCore(path, request, requestMethod); return new UnaryResult(future); } async Task> InvokeAsyncCore(string path, TRequest request, Func requestMethod) { if (this.option.Headers == null && filters.Length != 0) { // always creating new Metadata is bad manner for performance this.option = this.option.WithHeaders(new Metadata()); } var requestContext = new RequestContext(request, this, path, option, typeof(TResponse), filters, requestMethod); var response = await InterceptInvokeHelper.InvokeWithFilter(requestContext); var result = response as IResponseContext; if (result != null) { return result; } else { throw new InvalidOperationException("ResponseContext is null."); } } protected async Task> InvokeTaskAsync(string path, TRequest request, Func requestMethod) { if (this.option.Headers == null && filters.Length != 0) { // always creating new Metadata is bad manner for performance this.option = this.option.WithHeaders(new Metadata()); } var requestContext = new RequestContext(request, this, path, option, typeof(TResponse), filters, requestMethod); var response = await InterceptInvokeHelper.InvokeWithFilter(requestContext); var result = response as IResponseContext; if (result != null) { return new UnaryResult(Task.FromResult(result)); } else { throw new InvalidOperationException("ResponseContext is null."); } } protected abstract MagicOnionClientBase Clone(); public virtual T WithCancellationToken(CancellationToken cancellationToken) { return WithOptions(this.option.WithCancellationToken(cancellationToken)); } public virtual T WithDeadline(DateTime deadline) { return WithOptions(this.option.WithDeadline(deadline)); } public virtual T WithHeaders(Metadata headers) { return WithOptions(this.option.WithHeaders(headers)); } public virtual T WithHost(string host) { var newInstance = Clone(); newInstance.host = host; return (T)(object)newInstance; } public virtual T WithOptions(CallOptions option) { var newInstance = Clone(); newInstance.option = option; return (T)(object)newInstance; } } }