/// <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); }
/// <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); }
/// <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); }
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); }
// 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); } } }
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; } }
// 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); } } }