Esempio n. 1
0
        /// <summary>Register IAsyncRequestHandler[TRequest, TResponse] to container builder.</summary>
        public BuiltinContainerBuilder AddAsyncRequestHandler <TRequest, TResponse, THandler>()
            where THandler : IAsyncRequestHandler
        {
            var services = this;

            services.AddSingleton(typeof(IAsyncRequestHandlerCore <TRequest, TResponse>), typeof(THandler));
            services.AddSingleton(typeof(IAsyncRequestHandler <TRequest, TResponse>), typeof(AsyncRequestHandler <TRequest, TResponse>));

            AsyncRequestHandlerRegistory.Add(typeof(TRequest), typeof(TResponse), typeof(THandler));

            return(this);
        }
Esempio n. 2
0
        /// <summary>Register IAsyncRequestHandler[TRequest, TResponse](includes All) to container builder.</summary>
        public static IContainerBuilder RegisterAsyncRequestHandler <TRequest, TResponse, THandler>(this IContainerBuilder builder, MessagePipeOptions options)
            where THandler : IAsyncRequestHandler
        {
            var lifetime = GetLifetime(options.RequestHandlerLifetime);
            var services = new ContainerBuilderProxy(builder);

            services.Add(typeof(IAsyncRequestHandlerCore <TRequest, TResponse>), typeof(THandler), lifetime);
            if (!builder.Exists(typeof(IAsyncRequestHandler <TRequest, TResponse>), true))
            {
                services.Add(typeof(IAsyncRequestHandler <TRequest, TResponse>), typeof(AsyncRequestHandler <TRequest, TResponse>), lifetime);
                services.Add(typeof(IAsyncRequestAllHandler <TRequest, TResponse>), typeof(AsyncRequestAllHandler <TRequest, TResponse>), lifetime);
            }

            AsyncRequestHandlerRegistory.Add(typeof(TRequest), typeof(TResponse), typeof(THandler));
            return(builder);
        }
Esempio n. 3
0
        /// <summary>Register IAsyncRequestHandler[TRequest, TResponse](includes All) to container builder.</summary>
        public static DiContainer BindAsyncRequestHandler <TRequest, TResponse, THandler>(this DiContainer builder, MessagePipeOptions options)
            where THandler : IAsyncRequestHandler
        {
            var lifetime = options.RequestHandlerLifetime;
            var services = new DiContainerProxy(builder);

            services.Add(typeof(IAsyncRequestHandlerCore <TRequest, TResponse>), typeof(THandler), lifetime);
            if (!builder.HasBinding <IAsyncRequestHandler <TRequest, TResponse> >())
            {
                services.Add(typeof(IAsyncRequestHandler <TRequest, TResponse>), typeof(AsyncRequestHandler <TRequest, TResponse>), lifetime);
                services.Add(typeof(IAsyncRequestAllHandler <TRequest, TResponse>), typeof(AsyncRequestAllHandler <TRequest, TResponse>), lifetime);
            }

            AsyncRequestHandlerRegistory.Add(typeof(TRequest), typeof(TResponse), typeof(THandler));
            return(builder);
        }
Esempio n. 4
0
        static IServiceCollection AddRequestHandlerCore(IServiceCollection services, Type type, Type coreType)
        {
            var options = services.FirstOrDefault(x => x.ServiceType == typeof(MessagePipeOptions));

            if (options == null)
            {
                throw new ArgumentException($"Not yet added MessagePipeOptions, please call servcies.AddMessagePipe() before.");
            }
            var isAsync = (coreType == typeof(IAsyncRequestHandlerCore <,>));

            var registered = false;

            foreach (var interfaceType in type.GetInterfaces())
            {
                if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == coreType)
                {
                    var iType = interfaceType;
                    if (type.IsGenericType && !type.IsConstructedGenericType)
                    {
                        if (!interfaceType.GetGenericArguments().All(x => x.IsGenericParameter))
                        {
                            throw new ArgumentException("partial open generic type is not supported. Type:" + type.FullName);
                        }

                        iType = interfaceType.GetGenericTypeDefinition();
                    }

                    registered = true;
                    services.Add(iType, type, ((MessagePipeOptions)options.ImplementationInstance !).RequestHandlerLifetime);
                }
            }

            if (!registered)
            {
                throw new ArgumentException($"{type.FullName} does not implement {coreType.Name.Replace("Core", "")}.");
            }
            else if (isAsync)
            {
                AsyncRequestHandlerRegistory.Add(coreType);
            }

            return(services);
        }
Esempio n. 5
0
        // Receive from udp socket and push value to subscribers.
        async void RunReceiveLoop(Stream pipeStream, Func <CancellationToken, Task>?waitForConnection)
        {
RECONNECT:
            var token = cancellationTokenSource.Token;

            if (waitForConnection != null)
            {
                try
                {
                    await waitForConnection(token).ConfigureAwait(false);
                }
                catch (IOException)
                {
                    return; // connection closed.
                }
            }
            var buffer = new byte[65536];

            while (!token.IsCancellationRequested)
            {
                ReadOnlyMemory <byte> value = Array.Empty <byte>();
                try
                {
                    var readLen = await pipeStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                    if (readLen == 0)
                    {
                        if (waitForConnection != null)
                        {
                            server.Value.Dispose();
                            server     = CreateLazyServerStream();
                            pipeStream = server.Value;
                            goto RECONNECT; // end of stream(disconnect, wait reconnect)
                        }
                    }

                    var messageLen = MessageBuilder.FetchMessageLength(buffer);
                    if (readLen == (messageLen + 4))
                    {
                        value = buffer.AsMemory(4, messageLen); // skip length header
                    }
                    else
                    {
                        // read more
                        if (buffer.Length < (messageLen + 4))
                        {
                            Array.Resize(ref buffer, messageLen + 4);
                        }
                        var remain = messageLen - (readLen - 4);
                        await ReadFullyAsync(buffer, pipeStream, readLen, remain, token).ConfigureAwait(false);

                        value = buffer.AsMemory(4, messageLen);
                    }
                }
                catch (IOException)
                {
                    return; // connection closed.
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        return;
                    }
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }

                    // network error, terminate.
                    options.UnhandledErrorHandler("network error, receive loop will terminate." + Environment.NewLine, ex);
                    return;
                }

                try
                {
                    var message = MessageBuilder.ReadPubSubMessage(value.ToArray()); // can avoid copy?
                    switch (message.MessageType)
                    {
                    case MessageType.PubSub:
                        publisher.Publish(message, message, CancellationToken.None);
                        break;

                    case MessageType.RemoteRequest:
                    {
                        // NOTE: should use without reflection(Expression.Compile)
                        var header = Deserialize <RequestHeader>(message.KeyMemory, options.MessagePackSerializerOptions);
                        var(mid, reqTypeName, resTypeName) = (header.MessageId, header.RequestType, header.ResponseType);
                        byte[] resultBytes;
                        try
                        {
                            var t             = AsyncRequestHandlerRegistory.Get(reqTypeName, resTypeName);
                            var interfaceType = t.GetInterfaces().Where(x => x.IsGenericType && x.Name.StartsWith("IAsyncRequestHandler"))
                                                .First(x => x.GetGenericArguments().Any(x => x.FullName == header.RequestType));
                            var coreInterfaceType = t.GetInterfaces().Where(x => x.IsGenericType && x.Name.StartsWith("IAsyncRequestHandlerCore"))
                                                    .First(x => x.GetGenericArguments().Any(x => x.FullName == header.RequestType));
                            var service      = provider.GetRequiredService(interfaceType); // IAsyncRequestHandler<TRequest,TResponse>
                            var genericArgs  = interfaceType.GetGenericArguments();        // [TRequest, TResponse]
                            var request      = MessagePackSerializer.Deserialize(genericArgs[0], message.ValueMemory, options.MessagePackSerializerOptions);
                            var responseTask = coreInterfaceType.GetMethod("InvokeAsync") !.Invoke(service, new[] { request, CancellationToken.None });
                            var task         = typeof(ValueTask <>).MakeGenericType(genericArgs[1]).GetMethod("AsTask") !.Invoke(responseTask, null);
                            await((System.Threading.Tasks.Task)task !);         // Task<T> -> Task
                            var result = task.GetType().GetProperty("Result") !.GetValue(task);
                            resultBytes = MessageBuilder.BuildRemoteResponseMessage(mid, genericArgs[1], result !, options.MessagePackSerializerOptions);
                        }
                        catch (Exception ex)
                        {
                            // NOTE: ok to send stacktrace?
                            resultBytes = MessageBuilder.BuildRemoteResponseError(mid, ex.ToString(), options.MessagePackSerializerOptions);
                        }

                        await pipeStream.WriteAsync(resultBytes, 0, resultBytes.Length).ConfigureAwait(false);
                    }
                    break;

                    case MessageType.RemoteResponse:
                    case MessageType.RemoteError:
                    {
                        var mid = Deserialize <int>(message.KeyMemory, options.MessagePackSerializerOptions);
                        if (responseCompletions.TryRemove(mid, out var tcs))
                        {
                            if (message.MessageType == MessageType.RemoteResponse)
                            {
                                tcs.TrySetResult(message);         // synchronous completion, use memory buffer immediately.
                            }
                            else
                            {
                                var errorMsg = MessagePackSerializer.Deserialize <string>(message.ValueMemory, options.MessagePackSerializerOptions);
                                tcs.TrySetException(new RemoteRequestException(errorMsg));
                            }
                        }
                    }
                    break;

                    default:
                        break;
                    }
                }
                catch (IOException)
                {
                    return; // connection closed.
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        continue;
                    }
                    options.UnhandledErrorHandler("", ex);
                }
            }
        }
Esempio n. 6
0
        static void AddRequestHandlerAndFilterFromTypes(IServiceCollection services, InstanceLifetime requestHandlerLifetime, IEnumerable <Type> targetTypes)
        {
            foreach (var objectType in targetTypes)
            {
                if (objectType.IsInterface || objectType.IsAbstract)
                {
                    continue;
                }
                if (objectType.GetCustomAttributes(typeof(IgnoreAutoRegistration), false).Length != 0)
                {
                    continue;
                }

                foreach (var interfaceType in objectType.GetInterfaces())
                {
                    if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IRequestHandlerCore <,>))
                    {
                        if (!objectType.IsGenericType || objectType.IsConstructedGenericType)
                        {
                            services.Add(interfaceType, objectType, requestHandlerLifetime);
                        }
                        else if (interfaceType.GetGenericArguments().All(x => x.IsGenericParameter))
                        {
                            services.Add(typeof(IRequestHandlerCore <,>), objectType, requestHandlerLifetime);
                        }
                        continue;
                    }

                    if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IAsyncRequestHandlerCore <,>))
                    {
                        if (!objectType.IsGenericType || objectType.IsConstructedGenericType)
                        {
                            services.Add(interfaceType, objectType, requestHandlerLifetime);
                        }
                        else if (interfaceType.GetGenericArguments().All(x => x.IsGenericParameter))
                        {
                            services.Add(typeof(IAsyncRequestHandlerCore <,>), objectType, requestHandlerLifetime);
                        }

                        AsyncRequestHandlerRegistory.Add(objectType);
                        continue;
                    }
                }

                foreach (var baseType in objectType.GetBaseTypes())
                {
                    if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(MessageHandlerFilter <>))
                    {
                        services.TryAddTransient(objectType);
                        goto NEXT_TYPE;
                    }

                    if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(AsyncMessageHandlerFilter <>))
                    {
                        services.TryAddTransient(objectType);
                        goto NEXT_TYPE;
                    }

                    if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(RequestHandlerFilter <,>))
                    {
                        services.TryAddTransient(objectType);
                        goto NEXT_TYPE;
                    }

                    if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(AsyncRequestHandlerFilter <,>))
                    {
                        services.TryAddTransient(objectType);
                        goto NEXT_TYPE;
                    }
                }

NEXT_TYPE:
                continue;
            }
        }
Esempio n. 7
0
        // Receive from tcp socket and push value to subscribers.
        async void RunReceiveLoop(SocketTcpClient client)
        {
            var token  = cancellationTokenSource.Token;
            var buffer = new byte[65536];
            ReadOnlyMemory <byte> readBuffer = Array.Empty <byte>();

            while (!token.IsCancellationRequested)
            {
                ReadOnlyMemory <byte> value = Array.Empty <byte>();
                try
                {
                    if (readBuffer.Length == 0)
                    {
                        var readLen = await client.ReceiveAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                        if (readLen == 0)
                        {
                            return;               // end of stream(disconnect)
                        }
                        readBuffer = buffer.AsMemory(0, readLen);
                    }
                    else if (readBuffer.Length < 4) // rare case
                    {
                        var readLen = await client.ReceiveAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                        if (readLen == 0)
                        {
                            return;
                        }
                        var newBuffer = new byte[readBuffer.Length + readLen];
                        readBuffer.CopyTo(newBuffer);
                        buffer.AsSpan(readLen).CopyTo(newBuffer.AsSpan(readBuffer.Length));
                        readBuffer = newBuffer;
                    }

                    var messageLen = MessageBuilder.FetchMessageLength(readBuffer.Span);
                    if (readBuffer.Length == (messageLen + 4))        // just size
                    {
                        value      = readBuffer.Slice(4, messageLen); // skip length header
                        readBuffer = Array.Empty <byte>();
                        goto PARSE_MESSAGE;
                    }
                    else if (readBuffer.Length > (messageLen + 4)) // over size
                    {
                        value      = readBuffer.Slice(4, messageLen);
                        readBuffer = readBuffer.Slice(messageLen + 4);
                        goto PARSE_MESSAGE;
                    }
                    else // needs to read more
                    {
                        var readLen = readBuffer.Length;
                        if (readLen < (messageLen + 4))
                        {
                            if (readBuffer.Length != buffer.Length)
                            {
                                var newBuffer = new byte[buffer.Length];
                                readBuffer.CopyTo(newBuffer);
                                buffer = newBuffer;
                            }

                            if (buffer.Length < messageLen + 4)
                            {
                                Array.Resize(ref buffer, messageLen + 4);
                            }
                        }
                        var remain = messageLen - (readLen - 4);
                        await ReadFullyAsync(buffer, client, readLen, remain, token).ConfigureAwait(false);

                        value      = buffer.AsMemory(4, messageLen);
                        readBuffer = Array.Empty <byte>();
                        goto PARSE_MESSAGE;
                    }
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        return;
                    }
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }

                    // network error, terminate.
                    options.UnhandledErrorHandler("network error, receive loop will terminate." + Environment.NewLine, ex);
                    return;
                }
PARSE_MESSAGE:
                try
                {
                    var message = MessageBuilder.ReadPubSubMessage(value.ToArray()); // can avoid copy?
                    switch (message.MessageType)
                    {
                    case MessageType.PubSub:
                        publisher.Publish(message, message, CancellationToken.None);
                        break;

                    case MessageType.RemoteRequest:
                    {
                        // NOTE: should use without reflection(Expression.Compile)
                        var header = Deserialize <RequestHeader>(message.KeyMemory, options.MessagePackSerializerOptions);
                        var(mid, reqTypeName, resTypeName) = (header.MessageId, header.RequestType, header.ResponseType);
                        byte[] resultBytes;
                        try
                        {
                            var t                 = AsyncRequestHandlerRegistory.Get(reqTypeName, resTypeName);
                            var interfaceType     = t.GetInterfaces().First(x => x.IsGenericType && x.Name.StartsWith("IAsyncRequestHandler"));
                            var coreInterfaceType = t.GetInterfaces().First(x => x.IsGenericType && x.Name.StartsWith("IAsyncRequestHandlerCore"));
                            var service           = provider.GetRequiredService(interfaceType); // IAsyncRequestHandler<TRequest,TResponse>
                            var genericArgs       = interfaceType.GetGenericArguments();        // [TRequest, TResponse]
                            // Unity IL2CPP does not work(can not invoke nongenerics MessagePackSerializer)
                            var request      = MessagePackSerializer.Deserialize(genericArgs[0], message.ValueMemory, options.MessagePackSerializerOptions);
                            var responseTask = coreInterfaceType.GetMethod("InvokeAsync") !.Invoke(service, new[] { request, CancellationToken.None });
#if !UNITY_2018_3_OR_NEWER
                            var task = typeof(ValueTask <>).MakeGenericType(genericArgs[1]).GetMethod("AsTask") !.Invoke(responseTask, null);
#else
                            var asTask = typeof(UniTaskExtensions).GetMethods().First(x => x.IsGenericMethod && x.Name == "AsTask")
                                         .MakeGenericMethod(genericArgs[1]);
                            var task = asTask.Invoke(null, new[] { responseTask });
#endif
                            await((System.Threading.Tasks.Task)task !);         // Task<T> -> Task
                            var result = task.GetType().GetProperty("Result") !.GetValue(task);
                            resultBytes = MessageBuilder.BuildRemoteResponseMessage(mid, genericArgs[1], result !, options.MessagePackSerializerOptions);
                        }
                        catch (Exception ex)
                        {
                            // NOTE: ok to send stacktrace?
                            resultBytes = MessageBuilder.BuildRemoteResponseError(mid, ex.ToString(), options.MessagePackSerializerOptions);
                        }

                        await client.SendAsync(resultBytes).ConfigureAwait(false);
                    }
                    break;

                    case MessageType.RemoteResponse:
                    case MessageType.RemoteError:
                    {
                        var mid = Deserialize <int>(message.KeyMemory, options.MessagePackSerializerOptions);
                        if (responseCompletions.TryRemove(mid, out var tcs))
                        {
                            if (message.MessageType == MessageType.RemoteResponse)
                            {
                                tcs.TrySetResult(message);         // synchronous completion, use memory buffer immediately.
                            }
                            else
                            {
                                var errorMsg = MessagePackSerializer.Deserialize <string>(message.ValueMemory, options.MessagePackSerializerOptions);
                                tcs.TrySetException(new RemoteRequestException(errorMsg));
                            }
                        }
                    }
                    break;

                    default:
                        break;
                    }
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        continue;
                    }
                    options.UnhandledErrorHandler("", ex);
                }
            }
        }