From 230ba63242239cf5d34943bab0d530f9a977f189 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 25 Apr 2020 17:14:32 +0200 Subject: [PATCH 01/53] refactor: initial commit of v3 --- azure-pipelines.yml | 47 +++++ .../IpcServiceSample.ConsoleClient.csproj | 2 +- .../IpcServiceSample.ConsoleClient/Program.cs | 0 .../Certificates/cert.crt | 0 .../Certificates/key.pem | 0 .../ComputingService.cs | 0 .../IpcServiceSample.ConsoleServer.csproj | 24 +++ .../IpcServiceSample.ConsoleServer/Program.cs | 93 +++++++++ .../SystemService.cs | 0 .../TestService.cs | 0 .../Helpers/LoggingStream.cs | 0 .../Helpers/XorStream.cs | 0 .../IComputingService.cs | 0 .../ISystemService.cs | 0 .../ITestService.cs | 0 .../IpcServiceSample.ServiceContracts.csproj | 0 .../Certificates/cert.crt | 0 .../Certificates/key.pem | 0 .../IpcServiceSample.WebServer.csproj | 7 +- .../IpcServiceSample.WebServer/Program.cs | 0 .../IpcServiceSample.WebServer/Startup.cs | 3 +- samples/Samples/Samples.sln | 13 ++ src/Directory.Build.props | 2 +- src/IpcServiceFramework.sln | 134 ++++++++----- .../Certificates/server.pfx | Bin 4165 -> 0 bytes .../IpcServiceSample.ConsoleServer.csproj | 26 --- src/IpcServiceSample.ConsoleServer/Program.cs | 61 ------ .../IpcRequest.cs | 0 .../IpcResponse.cs | 6 +- .../IpcServerException.cs | 127 +++++++------ .../IpcServerUserCodeException.cs | 62 +++--- ...ng.IpcServiceFramework.Abstractions.csproj | 15 ++ .../Services/IIpcMessageSerializer.cs | 2 +- .../Services/IValueConverter.cs | 0 .../IIpcClient.cs | 27 +++ ...erviceFramework.Client.Abstractions.csproj | 19 ++ ...pcServiceFramework.Client.NamedPipe.csproj | 18 ++ .../NamedPipeIpcClient.cs} | 7 +- ...ipeIpcClientServiceCollectionExtensions.cs | 17 ++ ...Kang.IpcServiceFramework.Client.Tcp.csproj | 18 ++ .../TcpIpcClient.cs | 109 +++++++++++ .../TcpIpcClientOptions.cs | 14 ++ ...TcpIpcClientServiceCollectionExtensions.cs | 28 +++ .../{IpcServiceClient.cs => IpcClient.cs} | 22 ++- .../IpcClientServiceCollectionExtensions.cs | 32 ++++ .../IpcServiceClientBuilder.cs | 41 ---- .../JKang.IpcServiceFramework.Client.csproj | 3 +- ...edPipeIpcServiceClientBuilderExtensions.cs | 16 -- .../Tcp/TcpIpcServiceClient.cs | 117 ------------ .../TcpIpcServiceClientBuilderExtensions.cs | 70 ------- .../DefaultValueConverterTest.cs | 179 ++++++------------ .../Fixtures/ComplexType.cs | 8 + .../Fixtures/EnumType.cs | 8 + .../Fixtures/IComplexType.cs | 8 + ...Kang.IpcServiceFramework.Core.Tests.csproj | 18 +- .../IO/IpcReader.cs | 15 +- .../IO/IpcWriter.cs | 7 +- .../InternalServiceCollectionExtensions.cs | 19 ++ .../JKang.IpcServiceFramework.Core.csproj | 11 +- .../Services/DefaultIpcMessageSerializer.cs | 4 +- .../Services/DefaultValueConverter.cs | 5 + .../IIpcEndpoint.cs | 14 ++ .../IIpcHostBuilder.cs | 9 + .../IpcEndpointOptions.cs | 9 + .../IpcHostingConfigurationException.cs | 18 ++ ...rviceFramework.Hosting.Abstractions.csproj | 15 ++ .../GlobalSuppressions.cs | 8 + ...cServiceFramework.Hosting.NamedPipe.csproj | 19 ++ .../NamedPipeIpcHostBuilderExtensions.cs | 38 ++++ .../NamedPipeIpcServiceEndpoint.cs | 37 ++++ .../NamedPipeIpcServiceEndpointOptions.cs | 14 ++ .../GlobalSuppressions.cs | 8 + ...ang.IpcServiceFramework.Hosting.Tcp.csproj | 18 ++ .../TcpIpcEndpoint.cs | 53 ++++++ .../TcpIpcEndpointOptions.cs | 14 ++ .../TcpIpcHostBuilderExtensions.cs | 43 +++++ .../GenericHostBuilderExtensions.cs | 15 ++ .../GlobalSuppressions.cs | 8 + .../IpcEndpoint.cs} | 137 +++++++++----- .../IpcHostBuilder.cs | 38 ++++ .../IpcHostedService.cs | 40 ++++ .../JKang.IpcServiceFramework.Hosting.csproj | 20 ++ .../ContractTest.cs | 127 ------------- .../EdgeCaseTest.cs | 54 ------ .../FailureDetailTest.cs | 60 ------ .../ITestService.cs | 22 --- ...pcServiceFramework.IntegrationTests.csproj | 22 --- .../TestService.cs | 67 ------- .../ContractTest.cs | 84 ++++++++ ....IpcServiceFramework.NamedPipeTests.csproj | 22 +++ .../IIpcServiceBuilder.cs | 21 -- .../IIpcServiceHost.cs | 12 -- .../IpcServiceBuilder.cs | 38 ---- .../IpcServiceHost.cs | 50 ----- .../IpcServiceHostBuilder.cs | 28 --- .../IpcServiceOptions.cs | 10 - .../IpcServiceServiceCollectionExtensions.cs | 29 --- .../JKang.IpcServiceFramework.Server.csproj | 18 -- ...NamedPipeIpcServiceCollectionExtensions.cs | 25 --- .../NamedPipe/NamedPipeIpcServiceEndpoint.cs | 75 -------- ...amedPipeIpcServiceHostBuilderExtensions.cs | 15 -- .../NamedPipe/NamedPipeOptions.cs | 7 - .../Tcp/TcpConcurrencyOptions.cs | 16 -- .../Tcp/TcpIpcServiceCollectionExtensions.cs | 10 - .../Tcp/TcpIpcServiceEndpoint.cs | 164 ---------------- .../Tcp/TcpIpcServiceHostBuilderExtensions.cs | 147 -------------- .../ContractTest.cs | 86 +++++++++ .../JKang.IpcServiceFramework.TcpTests.csproj | 23 +++ .../Fixtures/ITestService.cs | 24 +++ .../IpcApplicationFactory.cs | 78 ++++++++ .../JKang.IpcServiceFramework.Testing.csproj | 20 ++ .../TestBase.cs | 23 +++ .../TestCases/ContractTestCase.cs | 148 +++++++++++++++ 113 files changed, 1866 insertions(+), 1698 deletions(-) create mode 100644 azure-pipelines.yml rename {src => samples}/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj (87%) rename {src => samples}/IpcServiceSample.ConsoleClient/Program.cs (100%) rename {src => samples}/IpcServiceSample.ConsoleServer/Certificates/cert.crt (100%) rename {src => samples}/IpcServiceSample.ConsoleServer/Certificates/key.pem (100%) rename {src => samples}/IpcServiceSample.ConsoleServer/ComputingService.cs (100%) create mode 100644 samples/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj create mode 100644 samples/IpcServiceSample.ConsoleServer/Program.cs rename {src => samples}/IpcServiceSample.ConsoleServer/SystemService.cs (100%) rename {src => samples}/IpcServiceSample.ConsoleServer/TestService.cs (100%) rename {src => samples}/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs (100%) rename {src => samples}/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs (100%) rename {src => samples}/IpcServiceSample.ServiceContracts/IComputingService.cs (100%) rename {src => samples}/IpcServiceSample.ServiceContracts/ISystemService.cs (100%) rename {src => samples}/IpcServiceSample.ServiceContracts/ITestService.cs (100%) rename {src => samples}/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj (100%) rename {src => samples}/IpcServiceSample.WebServer/Certificates/cert.crt (100%) rename {src => samples}/IpcServiceSample.WebServer/Certificates/key.pem (100%) rename {src => samples}/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj (77%) rename {src => samples}/IpcServiceSample.WebServer/Program.cs (100%) rename {src => samples}/IpcServiceSample.WebServer/Startup.cs (91%) create mode 100644 samples/Samples/Samples.sln delete mode 100644 src/IpcServiceSample.ConsoleServer/Certificates/server.pfx delete mode 100644 src/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj delete mode 100644 src/IpcServiceSample.ConsoleServer/Program.cs rename src/{JKang.IpcServiceFramework.Core => JKang.IpcServiceFramework.Abstractions}/IpcRequest.cs (100%) rename src/{JKang.IpcServiceFramework.Core => JKang.IpcServiceFramework.Abstractions}/IpcResponse.cs (91%) rename src/{JKang.IpcServiceFramework.Core => JKang.IpcServiceFramework.Abstractions}/IpcServerException.cs (94%) rename src/{JKang.IpcServiceFramework.Core => JKang.IpcServiceFramework.Abstractions}/IpcServerUserCodeException.cs (96%) create mode 100644 src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj rename src/{JKang.IpcServiceFramework.Core => JKang.IpcServiceFramework.Abstractions}/Services/IIpcMessageSerializer.cs (85%) rename src/{JKang.IpcServiceFramework.Core => JKang.IpcServiceFramework.Abstractions}/Services/IValueConverter.cs (100%) create mode 100644 src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs create mode 100644 src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj create mode 100644 src/JKang.IpcServiceFramework.Client.NamedPipe/JKang.IpcServiceFramework.Client.NamedPipe.csproj rename src/{JKang.IpcServiceFramework.Client/NamedPipe/NamedPipeIpcServiceClient.cs => JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs} (71%) create mode 100644 src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs create mode 100644 src/JKang.IpcServiceFramework.Client.Tcp/JKang.IpcServiceFramework.Client.Tcp.csproj create mode 100644 src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs create mode 100644 src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientOptions.cs create mode 100644 src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs rename src/JKang.IpcServiceFramework.Client/{IpcServiceClient.cs => IpcClient.cs} (89%) create mode 100644 src/JKang.IpcServiceFramework.Client/IpcClientServiceCollectionExtensions.cs delete mode 100644 src/JKang.IpcServiceFramework.Client/IpcServiceClientBuilder.cs delete mode 100644 src/JKang.IpcServiceFramework.Client/NamedPipe/NamedPipeIpcServiceClientBuilderExtensions.cs delete mode 100644 src/JKang.IpcServiceFramework.Client/Tcp/TcpIpcServiceClient.cs delete mode 100644 src/JKang.IpcServiceFramework.Client/Tcp/TcpIpcServiceClientBuilderExtensions.cs create mode 100644 src/JKang.IpcServiceFramework.Core.Tests/Fixtures/ComplexType.cs create mode 100644 src/JKang.IpcServiceFramework.Core.Tests/Fixtures/EnumType.cs create mode 100644 src/JKang.IpcServiceFramework.Core.Tests/Fixtures/IComplexType.cs create mode 100644 src/JKang.IpcServiceFramework.Core/InternalServiceCollectionExtensions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcHostBuilder.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcHostingConfigurationException.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj create mode 100644 src/JKang.IpcServiceFramework.Hosting.NamedPipe/GlobalSuppressions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj create mode 100644 src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpointOptions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Tcp/GlobalSuppressions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Tcp/JKang.IpcServiceFramework.Hosting.Tcp.csproj create mode 100644 src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpointOptions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting/GenericHostBuilderExtensions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting/GlobalSuppressions.cs rename src/{JKang.IpcServiceFramework.Server/IpcServiceEndpoint.cs => JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs} (61%) create mode 100644 src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj delete mode 100644 src/JKang.IpcServiceFramework.IntegrationTests/ContractTest.cs delete mode 100644 src/JKang.IpcServiceFramework.IntegrationTests/EdgeCaseTest.cs delete mode 100644 src/JKang.IpcServiceFramework.IntegrationTests/FailureDetailTest.cs delete mode 100644 src/JKang.IpcServiceFramework.IntegrationTests/ITestService.cs delete mode 100644 src/JKang.IpcServiceFramework.IntegrationTests/JKang.IpcServiceFramework.IntegrationTests.csproj delete mode 100644 src/JKang.IpcServiceFramework.IntegrationTests/TestService.cs create mode 100644 src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs create mode 100644 src/JKang.IpcServiceFramework.NamedPipeTests/JKang.IpcServiceFramework.NamedPipeTests.csproj delete mode 100644 src/JKang.IpcServiceFramework.Server/IIpcServiceBuilder.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/IIpcServiceHost.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/IpcServiceBuilder.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/IpcServiceHost.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/IpcServiceHostBuilder.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/IpcServiceOptions.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/IpcServiceServiceCollectionExtensions.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/JKang.IpcServiceFramework.Server.csproj delete mode 100644 src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceCollectionExtensions.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceEndpoint.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceHostBuilderExtensions.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeOptions.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/Tcp/TcpConcurrencyOptions.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceCollectionExtensions.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceEndpoint.cs delete mode 100644 src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceHostBuilderExtensions.cs create mode 100644 src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs create mode 100644 src/JKang.IpcServiceFramework.TcpTests/JKang.IpcServiceFramework.TcpTests.csproj create mode 100644 src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs create mode 100644 src/JKang.IpcServiceFramework.Testing/IpcApplicationFactory.cs create mode 100644 src/JKang.IpcServiceFramework.Testing/JKang.IpcServiceFramework.Testing.csproj create mode 100644 src/JKang.IpcServiceFramework.Testing/TestBase.cs create mode 100644 src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..3d9bdd4 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,47 @@ +# Set variables once +variables: + configuration: release + assemblyVersion: 2.0.0.0 + packageVersion: 2.0.3 + +resources: +- repo: self + clean: true +queue: + name: Hosted VS2017 +#Your build pipeline references the ‘BuildConfiguration’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971 +#Your build pipeline references the ‘AssemblyVersion’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971 +#Your build pipeline references an undefined variable named ‘Parameters.TestProjects’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972 +#Your build pipeline references the ‘BuildConfiguration’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971 +#Your build pipeline references the ‘AssemblyVersion’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971 +steps: +- task: DotNetCoreCLI@2 + displayName: Build + inputs: + projects: 'src/IpcServiceFramework.sln' + arguments: '--configuration $(configuration) /p:Version=$(assemblyVersion)' + + +- task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: test + projects: 'src/JKang.IpcServiceFramework.Core.Tests' + arguments: '--configuration $(configuration)' + + +- task: DotNetCoreCLI@2 + displayName: Pack + inputs: + command: pack + packagesToPack: '**/JKang.IpcServiceFramework*.csproj' + nobuild: true + versioningScheme: byEnvVar + versionEnvVar: packageVersion + +- task: PublishBuildArtifacts@1 + displayName: 'Publish Artifact' + inputs: + PathtoPublish: '$(build.artifactstagingdirectory)' + + diff --git a/src/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj b/samples/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj similarity index 87% rename from src/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj rename to samples/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj index ea4d846..b8d0597 100644 --- a/src/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj +++ b/samples/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp3.1 diff --git a/src/IpcServiceSample.ConsoleClient/Program.cs b/samples/IpcServiceSample.ConsoleClient/Program.cs similarity index 100% rename from src/IpcServiceSample.ConsoleClient/Program.cs rename to samples/IpcServiceSample.ConsoleClient/Program.cs diff --git a/src/IpcServiceSample.ConsoleServer/Certificates/cert.crt b/samples/IpcServiceSample.ConsoleServer/Certificates/cert.crt similarity index 100% rename from src/IpcServiceSample.ConsoleServer/Certificates/cert.crt rename to samples/IpcServiceSample.ConsoleServer/Certificates/cert.crt diff --git a/src/IpcServiceSample.ConsoleServer/Certificates/key.pem b/samples/IpcServiceSample.ConsoleServer/Certificates/key.pem similarity index 100% rename from src/IpcServiceSample.ConsoleServer/Certificates/key.pem rename to samples/IpcServiceSample.ConsoleServer/Certificates/key.pem diff --git a/src/IpcServiceSample.ConsoleServer/ComputingService.cs b/samples/IpcServiceSample.ConsoleServer/ComputingService.cs similarity index 100% rename from src/IpcServiceSample.ConsoleServer/ComputingService.cs rename to samples/IpcServiceSample.ConsoleServer/ComputingService.cs diff --git a/samples/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj b/samples/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj new file mode 100644 index 0000000..ebc395d --- /dev/null +++ b/samples/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj @@ -0,0 +1,24 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/samples/IpcServiceSample.ConsoleServer/Program.cs b/samples/IpcServiceSample.ConsoleServer/Program.cs new file mode 100644 index 0000000..c981087 --- /dev/null +++ b/samples/IpcServiceSample.ConsoleServer/Program.cs @@ -0,0 +1,93 @@ +using IpcServiceSample.ServiceContracts; +using IpcServiceSample.ServiceContracts.Helpers; +using JKang.IpcServiceFramework; +using JKang.IpcServiceFramework.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; + +namespace IpcServiceSample.ConsoleServer +{ + class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureServices((_, services) => + { + services + .AddScoped() + .AddScoped() + .AddScoped() + ; + }) + .ConfigureIpcHost(builder => + { + builder + .AddNamedPipeEndpoint("endpoint1", "pipeName") + .AddNamedPipeEndpoint("endpoint2", "pipeName2") + ; + }) + .ConfigureLogging(builder => + { + builder + .AddConsole() + .SetMinimumLevel(LogLevel.Debug); + }) + ; + + //static void Main(string[] args) + //{ + // // configure DI + // IServiceCollection services = ConfigureServices(new ServiceCollection()); + + // // build and run service host + // IIpcServiceHost host = new IpcServiceHostBuilder(services.BuildServiceProvider()) + // .AddNamedPipeEndpoint("computingEndpoint", "pipeName") + // .AddTcpEndpoint("systemEndpoint", IPAddress.Loopback, 45684) + // .AddTcpEndpoint("secureEndpoint", IPAddress.Loopback, 44384, new X509Certificate2(@"Certificates\server.pfx", "password")) + // .AddTcpEndpoint("xorTranslatedEndpoint", IPAddress.Loopback, 45454, s => new XorStream(s)) + // .Build(); + + // var source = new CancellationTokenSource(); + // Task.WaitAll(host.RunAsync(source.Token), Task.Run(() => + // { + // Console.WriteLine("Press any key to shutdown."); + // Console.ReadKey(); + // source.Cancel(); + // })); + + // Console.WriteLine("Server stopped."); + //} + + //private static IServiceCollection ConfigureServices(IServiceCollection services) + //{ + // return services + // .AddLogging(builder => + // { + // builder.AddConsole(); + // builder.SetMinimumLevel(LogLevel.Debug); + // }) + // .AddIpc(builder => + // { + // builder + // .AddNamedPipe(options => + // { + // options.ThreadCount = 2; + // }) + // .AddService() + // .AddService() + // .AddService(); + // }); + //} + } +} diff --git a/src/IpcServiceSample.ConsoleServer/SystemService.cs b/samples/IpcServiceSample.ConsoleServer/SystemService.cs similarity index 100% rename from src/IpcServiceSample.ConsoleServer/SystemService.cs rename to samples/IpcServiceSample.ConsoleServer/SystemService.cs diff --git a/src/IpcServiceSample.ConsoleServer/TestService.cs b/samples/IpcServiceSample.ConsoleServer/TestService.cs similarity index 100% rename from src/IpcServiceSample.ConsoleServer/TestService.cs rename to samples/IpcServiceSample.ConsoleServer/TestService.cs diff --git a/src/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs b/samples/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs similarity index 100% rename from src/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs rename to samples/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs diff --git a/src/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs b/samples/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs similarity index 100% rename from src/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs rename to samples/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs diff --git a/src/IpcServiceSample.ServiceContracts/IComputingService.cs b/samples/IpcServiceSample.ServiceContracts/IComputingService.cs similarity index 100% rename from src/IpcServiceSample.ServiceContracts/IComputingService.cs rename to samples/IpcServiceSample.ServiceContracts/IComputingService.cs diff --git a/src/IpcServiceSample.ServiceContracts/ISystemService.cs b/samples/IpcServiceSample.ServiceContracts/ISystemService.cs similarity index 100% rename from src/IpcServiceSample.ServiceContracts/ISystemService.cs rename to samples/IpcServiceSample.ServiceContracts/ISystemService.cs diff --git a/src/IpcServiceSample.ServiceContracts/ITestService.cs b/samples/IpcServiceSample.ServiceContracts/ITestService.cs similarity index 100% rename from src/IpcServiceSample.ServiceContracts/ITestService.cs rename to samples/IpcServiceSample.ServiceContracts/ITestService.cs diff --git a/src/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj b/samples/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj similarity index 100% rename from src/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj rename to samples/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj diff --git a/src/IpcServiceSample.WebServer/Certificates/cert.crt b/samples/IpcServiceSample.WebServer/Certificates/cert.crt similarity index 100% rename from src/IpcServiceSample.WebServer/Certificates/cert.crt rename to samples/IpcServiceSample.WebServer/Certificates/cert.crt diff --git a/src/IpcServiceSample.WebServer/Certificates/key.pem b/samples/IpcServiceSample.WebServer/Certificates/key.pem similarity index 100% rename from src/IpcServiceSample.WebServer/Certificates/key.pem rename to samples/IpcServiceSample.WebServer/Certificates/key.pem diff --git a/src/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj b/samples/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj similarity index 77% rename from src/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj rename to samples/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj index 3f2bc42..2eec263 100644 --- a/src/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj +++ b/samples/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj @@ -1,16 +1,11 @@ - netcoreapp2.0 + netcoreapp3.1 - - - - - diff --git a/src/IpcServiceSample.WebServer/Program.cs b/samples/IpcServiceSample.WebServer/Program.cs similarity index 100% rename from src/IpcServiceSample.WebServer/Program.cs rename to samples/IpcServiceSample.WebServer/Program.cs diff --git a/src/IpcServiceSample.WebServer/Startup.cs b/samples/IpcServiceSample.WebServer/Startup.cs similarity index 91% rename from src/IpcServiceSample.WebServer/Startup.cs rename to samples/IpcServiceSample.WebServer/Startup.cs index 29df801..cdd046c 100644 --- a/src/IpcServiceSample.WebServer/Startup.cs +++ b/samples/IpcServiceSample.WebServer/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace IpcServiceSample.WebServer { @@ -23,7 +24,7 @@ public void ConfigureServices(IServiceCollection services) }); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { diff --git a/samples/Samples/Samples.sln b/samples/Samples/Samples.sln new file mode 100644 index 0000000..e70cf5a --- /dev/null +++ b/samples/Samples/Samples.sln @@ -0,0 +1,13 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Global + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F174C67D-CEF7-4016-8DB6-94ED131FF8E6} + EndGlobalSection +EndGlobal diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7ebd895..7ab2261 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -8,7 +8,7 @@ dotnetcore,named-pipes,interprocess-communication https://github.com/jacqueskang/IpcServiceFramework - 0.0.0 + 2.0.3 \ No newline at end of file diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index 292b4cd..84f46ed 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -3,14 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28803.156 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{03210BFB-17B1-4775-A8A2-D302ECBF2F46}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.ServiceContracts", "IpcServiceSample.ServiceContracts\IpcServiceSample.ServiceContracts.csproj", "{D1ADB92C-B4FB-40DD-911E-46360A23381B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.ConsoleServer", "IpcServiceSample.ConsoleServer\IpcServiceSample.ConsoleServer.csproj", "{24A3C4D2-95A2-48D9-86F2-648879EC74F4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.ConsoleClient", "IpcServiceSample.ConsoleClient\IpcServiceSample.ConsoleClient.csproj", "{8D54E62A-ECFF-4FFF-B9D1-DB343D456451}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20913218-C740-42E9-9D17-CAD973B676D0}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig @@ -19,17 +11,37 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\README.md = ..\README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client", "JKang.IpcServiceFramework.Client\JKang.IpcServiceFramework.Client.csproj", "{13D724CA-3FEA-49E1-BB6B-365CF9397986}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Core.Tests", "JKang.IpcServiceFramework.Core.Tests\JKang.IpcServiceFramework.Core.Tests.csproj", "{1EC81913-883B-487C-A3FD-98A80EDE3225}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Core", "JKang.IpcServiceFramework.Core\JKang.IpcServiceFramework.Core.csproj", "{58200319-1F71-4E22-894D-7E69E0CD0B57}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Hosting.Abstractions", "JKang.IpcServiceFramework.Hosting.Abstractions\JKang.IpcServiceFramework.Hosting.Abstractions.csproj", "{85324785-234E-4D25-806D-BE7B69800CFD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Server", "JKang.IpcServiceFramework.Server\JKang.IpcServiceFramework.Server.csproj", "{069B416A-B2C6-40D1-80B8-AC9ACA2217E3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Hosting.NamedPipe", "JKang.IpcServiceFramework.Hosting.NamedPipe\JKang.IpcServiceFramework.Hosting.NamedPipe.csproj", "{42A743C1-2E99-4D3B-BA77-ACDF8309DC74}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Core.Tests", "JKang.IpcServiceFramework.Core.Tests\JKang.IpcServiceFramework.Core.Tests.csproj", "{1EC81913-883B-487C-A3FD-98A80EDE3225}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NamedPipe", "NamedPipe", "{7ED117EC-C390-4D2E-94D3-C00010B63535}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Client.NamedPipe", "JKang.IpcServiceFramework.Client.NamedPipe\JKang.IpcServiceFramework.Client.NamedPipe.csproj", "{3BCFFC74-2722-43DC-B910-92A34997FA2B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tcp", "Tcp", "{7A7B0FCA-ADE5-4C7A-9640-9E10821D251C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Hosting.Tcp", "JKang.IpcServiceFramework.Hosting.Tcp\JKang.IpcServiceFramework.Hosting.Tcp.csproj", "{2783640E-5E3D-447A-BEC2-C1A74E09089C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Client.Tcp", "JKang.IpcServiceFramework.Client.Tcp\JKang.IpcServiceFramework.Client.Tcp.csproj", "{E1EE30C2-EF31-47C9-B9FC-D55A94911C40}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Abstractions", "JKang.IpcServiceFramework.Abstractions\JKang.IpcServiceFramework.Abstractions.csproj", "{F8E7A89B-F863-42FF-9FE0-77A107938454}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client", "JKang.IpcServiceFramework.Client\JKang.IpcServiceFramework.Client.csproj", "{46465A56-CF19-4403-A78C-9871BFD8B49E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Core", "JKang.IpcServiceFramework.Core\JKang.IpcServiceFramework.Core.csproj", "{0713BDCA-E193-4333-988E-B8EE196BA0E6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Hosting", "JKang.IpcServiceFramework.Hosting\JKang.IpcServiceFramework.Hosting.csproj", "{5C70C051-D841-4F4C-8C11-E34B74674187}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client.Abstractions", "JKang.IpcServiceFramework.Client.Abstractions\JKang.IpcServiceFramework.Client.Abstractions.csproj", "{AE3DE1F9-EAF2-4785-9A23-E03F40D52156}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Testing", "JKang.IpcServiceFramework.Testing\JKang.IpcServiceFramework.Testing.csproj", "{915000BD-9D84-4554-9A91-428923060EC5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.WebServer", "IpcServiceSample.WebServer\IpcServiceSample.WebServer.csproj", "{D57727B9-81F1-439A-AD17-0DB26C8F0523}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.TcpTests", "JKang.IpcServiceFramework.TcpTests\JKang.IpcServiceFramework.TcpTests.csproj", "{C6625102-AC1B-4D86-8BF8-B2096F701AC2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.IntegrationTests", "JKang.IpcServiceFramework.IntegrationTests\JKang.IpcServiceFramework.IntegrationTests.csproj", "{451DE9A3-7A34-487B-823C-FB3C6B6EF20D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.NamedPipeTests", "JKang.IpcServiceFramework.NamedPipeTests\JKang.IpcServiceFramework.NamedPipeTests.csproj", "{62A037D0-4FC0-4EF8-A87F-ABC93209EB46}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -37,51 +49,73 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D1ADB92C-B4FB-40DD-911E-46360A23381B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1ADB92C-B4FB-40DD-911E-46360A23381B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1ADB92C-B4FB-40DD-911E-46360A23381B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1ADB92C-B4FB-40DD-911E-46360A23381B}.Release|Any CPU.Build.0 = Release|Any CPU - {24A3C4D2-95A2-48D9-86F2-648879EC74F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24A3C4D2-95A2-48D9-86F2-648879EC74F4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24A3C4D2-95A2-48D9-86F2-648879EC74F4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24A3C4D2-95A2-48D9-86F2-648879EC74F4}.Release|Any CPU.Build.0 = Release|Any CPU - {8D54E62A-ECFF-4FFF-B9D1-DB343D456451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8D54E62A-ECFF-4FFF-B9D1-DB343D456451}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8D54E62A-ECFF-4FFF-B9D1-DB343D456451}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8D54E62A-ECFF-4FFF-B9D1-DB343D456451}.Release|Any CPU.Build.0 = Release|Any CPU - {13D724CA-3FEA-49E1-BB6B-365CF9397986}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13D724CA-3FEA-49E1-BB6B-365CF9397986}.Debug|Any CPU.Build.0 = Debug|Any CPU - {13D724CA-3FEA-49E1-BB6B-365CF9397986}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13D724CA-3FEA-49E1-BB6B-365CF9397986}.Release|Any CPU.Build.0 = Release|Any CPU - {58200319-1F71-4E22-894D-7E69E0CD0B57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {58200319-1F71-4E22-894D-7E69E0CD0B57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {58200319-1F71-4E22-894D-7E69E0CD0B57}.Release|Any CPU.ActiveCfg = Release|Any CPU - {58200319-1F71-4E22-894D-7E69E0CD0B57}.Release|Any CPU.Build.0 = Release|Any CPU - {069B416A-B2C6-40D1-80B8-AC9ACA2217E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {069B416A-B2C6-40D1-80B8-AC9ACA2217E3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {069B416A-B2C6-40D1-80B8-AC9ACA2217E3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {069B416A-B2C6-40D1-80B8-AC9ACA2217E3}.Release|Any CPU.Build.0 = Release|Any CPU {1EC81913-883B-487C-A3FD-98A80EDE3225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1EC81913-883B-487C-A3FD-98A80EDE3225}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EC81913-883B-487C-A3FD-98A80EDE3225}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EC81913-883B-487C-A3FD-98A80EDE3225}.Release|Any CPU.Build.0 = Release|Any CPU - {D57727B9-81F1-439A-AD17-0DB26C8F0523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D57727B9-81F1-439A-AD17-0DB26C8F0523}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D57727B9-81F1-439A-AD17-0DB26C8F0523}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D57727B9-81F1-439A-AD17-0DB26C8F0523}.Release|Any CPU.Build.0 = Release|Any CPU - {451DE9A3-7A34-487B-823C-FB3C6B6EF20D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {451DE9A3-7A34-487B-823C-FB3C6B6EF20D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {451DE9A3-7A34-487B-823C-FB3C6B6EF20D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {451DE9A3-7A34-487B-823C-FB3C6B6EF20D}.Release|Any CPU.Build.0 = Release|Any CPU + {85324785-234E-4D25-806D-BE7B69800CFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85324785-234E-4D25-806D-BE7B69800CFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85324785-234E-4D25-806D-BE7B69800CFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85324785-234E-4D25-806D-BE7B69800CFD}.Release|Any CPU.Build.0 = Release|Any CPU + {42A743C1-2E99-4D3B-BA77-ACDF8309DC74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42A743C1-2E99-4D3B-BA77-ACDF8309DC74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42A743C1-2E99-4D3B-BA77-ACDF8309DC74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42A743C1-2E99-4D3B-BA77-ACDF8309DC74}.Release|Any CPU.Build.0 = Release|Any CPU + {3BCFFC74-2722-43DC-B910-92A34997FA2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BCFFC74-2722-43DC-B910-92A34997FA2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BCFFC74-2722-43DC-B910-92A34997FA2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BCFFC74-2722-43DC-B910-92A34997FA2B}.Release|Any CPU.Build.0 = Release|Any CPU + {2783640E-5E3D-447A-BEC2-C1A74E09089C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2783640E-5E3D-447A-BEC2-C1A74E09089C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2783640E-5E3D-447A-BEC2-C1A74E09089C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2783640E-5E3D-447A-BEC2-C1A74E09089C}.Release|Any CPU.Build.0 = Release|Any CPU + {E1EE30C2-EF31-47C9-B9FC-D55A94911C40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1EE30C2-EF31-47C9-B9FC-D55A94911C40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1EE30C2-EF31-47C9-B9FC-D55A94911C40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1EE30C2-EF31-47C9-B9FC-D55A94911C40}.Release|Any CPU.Build.0 = Release|Any CPU + {F8E7A89B-F863-42FF-9FE0-77A107938454}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8E7A89B-F863-42FF-9FE0-77A107938454}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8E7A89B-F863-42FF-9FE0-77A107938454}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8E7A89B-F863-42FF-9FE0-77A107938454}.Release|Any CPU.Build.0 = Release|Any CPU + {46465A56-CF19-4403-A78C-9871BFD8B49E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46465A56-CF19-4403-A78C-9871BFD8B49E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46465A56-CF19-4403-A78C-9871BFD8B49E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46465A56-CF19-4403-A78C-9871BFD8B49E}.Release|Any CPU.Build.0 = Release|Any CPU + {0713BDCA-E193-4333-988E-B8EE196BA0E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0713BDCA-E193-4333-988E-B8EE196BA0E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0713BDCA-E193-4333-988E-B8EE196BA0E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0713BDCA-E193-4333-988E-B8EE196BA0E6}.Release|Any CPU.Build.0 = Release|Any CPU + {5C70C051-D841-4F4C-8C11-E34B74674187}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C70C051-D841-4F4C-8C11-E34B74674187}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C70C051-D841-4F4C-8C11-E34B74674187}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C70C051-D841-4F4C-8C11-E34B74674187}.Release|Any CPU.Build.0 = Release|Any CPU + {AE3DE1F9-EAF2-4785-9A23-E03F40D52156}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE3DE1F9-EAF2-4785-9A23-E03F40D52156}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE3DE1F9-EAF2-4785-9A23-E03F40D52156}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE3DE1F9-EAF2-4785-9A23-E03F40D52156}.Release|Any CPU.Build.0 = Release|Any CPU + {915000BD-9D84-4554-9A91-428923060EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {915000BD-9D84-4554-9A91-428923060EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {915000BD-9D84-4554-9A91-428923060EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {915000BD-9D84-4554-9A91-428923060EC5}.Release|Any CPU.Build.0 = Release|Any CPU + {C6625102-AC1B-4D86-8BF8-B2096F701AC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C6625102-AC1B-4D86-8BF8-B2096F701AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6625102-AC1B-4D86-8BF8-B2096F701AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C6625102-AC1B-4D86-8BF8-B2096F701AC2}.Release|Any CPU.Build.0 = Release|Any CPU + {62A037D0-4FC0-4EF8-A87F-ABC93209EB46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62A037D0-4FC0-4EF8-A87F-ABC93209EB46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62A037D0-4FC0-4EF8-A87F-ABC93209EB46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62A037D0-4FC0-4EF8-A87F-ABC93209EB46}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {D1ADB92C-B4FB-40DD-911E-46360A23381B} = {03210BFB-17B1-4775-A8A2-D302ECBF2F46} - {24A3C4D2-95A2-48D9-86F2-648879EC74F4} = {03210BFB-17B1-4775-A8A2-D302ECBF2F46} - {8D54E62A-ECFF-4FFF-B9D1-DB343D456451} = {03210BFB-17B1-4775-A8A2-D302ECBF2F46} - {D57727B9-81F1-439A-AD17-0DB26C8F0523} = {03210BFB-17B1-4775-A8A2-D302ECBF2F46} + {42A743C1-2E99-4D3B-BA77-ACDF8309DC74} = {7ED117EC-C390-4D2E-94D3-C00010B63535} + {3BCFFC74-2722-43DC-B910-92A34997FA2B} = {7ED117EC-C390-4D2E-94D3-C00010B63535} + {2783640E-5E3D-447A-BEC2-C1A74E09089C} = {7A7B0FCA-ADE5-4C7A-9640-9E10821D251C} + {E1EE30C2-EF31-47C9-B9FC-D55A94911C40} = {7A7B0FCA-ADE5-4C7A-9640-9E10821D251C} + {C6625102-AC1B-4D86-8BF8-B2096F701AC2} = {7A7B0FCA-ADE5-4C7A-9640-9E10821D251C} + {62A037D0-4FC0-4EF8-A87F-ABC93209EB46} = {7ED117EC-C390-4D2E-94D3-C00010B63535} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F87E0D46-F461-4E41-9A3B-64710A6DFB2F} diff --git a/src/IpcServiceSample.ConsoleServer/Certificates/server.pfx b/src/IpcServiceSample.ConsoleServer/Certificates/server.pfx deleted file mode 100644 index 283517c9f7367ea1ab22309ebb205aa847c9e2a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4165 zcmY+GbyO3KyT{4Vj2A`^K~mBY(k)6z*I;x@DoBrp(It~^P>>F3Bt@hYkd&H(0V5?w zBVONg?!CWz|9H-G&gXlc=ikp45=EmzfKLdCq9Fm03B+o|UQpqa;Fm-JFNvanI7k%m z2oeS0`%eTYi2^YFMIZuvyuXX;KM6hr3?TpS50v--2sIJuo?ljo?P0ev0RbsqL=^GQ z&hVT7l(97ZJot$J(h9x{iuNj0X%_17QSWxx4pksna)JSq=Qpj4Nl45NWKl{N>43}dwKoF{c_sdckP76w%>#%%llv&cA551FrxSKm>B=Q( z@JEz|30#82;T3*b?hfyy;f0d+?3$Z0xbBC>tIX!YBMRa+eAm`={<7gvakQH`hkU_I zN3Ehg*7ui-r)mEMkY&xX-!|SgX`-&0Uv1klVSCv-;4T!}+a4rdtMd)~Z5een8PLZo%t!Gv6YZ&21~T@WF*kpJvGHD4PmlvzZzbAp=)BG;cZ; zoXR<8=p;Y-nV?eqi!70ZUqB9z2ET^!b7HZV)7pL&OlC~{*ZT7HK_uuOOLWFd8tz<- z%Sz6`YMAR5|LLTriH1dG!@M?$Yumlh${d@FAwkfKK*ZuVbXUdPHQNWy6h!VOiV|bn zx+86erG*oynW{E1!4c1w#2%Xnq1K5f5bJ9e2PXS6;qOV+rref5blYKzKg=CK3d#3k z3yMVpS(}G6&C2?MMaKHmYm|B|cparS%##6jk~Tx$vj(j~7163}$3Y4B>amx(S3Sewf7 zkc%f-1Ci?O#NwGpmtyQ=uWEr|;VU$zY=Yvo-5;QrpfuO*p_G5(cxR;Eqf&evp{|bE zcTX{`Z=l=dao^tQKco4joGNplp59+((?0W!5>h29e=Ey=P+%}>laW19UIu{m#HtdC zU015H7HuO*UFCR7lTkQR%EB_rKoS9QY&40cbA^u~mVJd0%np7iRjL0R+ez{5*5zM2{Vm<)h$-E@~tzHIN! z?|HA&t3jmJLK=T0eEeFBKMcZBNKqvr(?{O8RH$>Am!0>KO~|w3#m$dmLR%GgOs4#q zLc872?@|Wq2hRIixHhGURb4xUIF=@Pf%FFdl7i;F`^|IOlgjsOoWOxZ1x~jhB)jA4 zoH8~-yV8S>hM|7NaPUR$+u&T;tqTD?<9c>m4j=>7Ppw1u^q*fhWHz#y`kd+^7`iug z1G*8_k~>aTmiWE zYVWa!Ky|3x#})g6M0RIY^~h2bqKd356>l<6k91IqZn2v%ePLN`K?qF$+=5|dZ!a#u zp1_t6Zn^D&XJIk3Dm+Q%VW{v?)KKS)JkM?-sjT^;7G0jL1H7`4^`~J+;j}OVXcjVo zO(d+y!2-E%1hJXazRzm%L(7uqc7lLHwL@|2o@z2Yetg_ut+<*?RhuFewOHI-PHM;c z!qs>>oYcr{8`rVB)%-lpmCsETzVIDec_>F5oV>|x77}Z$C*$@hLwNS~Snd5{`_lK& zrEqJDo%)e>1???mJ|W-lM-Y1JMT|8x;!Gfk`sIrSv%iHGB?A))d@Estf&;fx?#JEw$ji-@lYGC7PwNkllJ&O9M`>;F2_5f z^W{Qp=ENum!8E>UYK0nMv?cDOoKmDT8i7F>UW&^;1l;B_2C>wX=DpGmADf<|Gc$}D zLSlTwzJFB{<`5mNJR+K-e{g4;4dAdIISvehD!0#&dYI-BPc$%a591=w~@PC9* z5=GWb6h&757gztC3IN6b=z$!c;BV;l7rOmFYY_fxjkxA&D%0ft^M9@J_gq6F{6TOL zls-k zH$*DNS}jI)@hDU8T9i9^3|=>4eJrl1XkBQ!dU{g>j4DPBb@G6b&jc2*Z;JfCK6w~K@JH8u$hf3d=fsqV`I@kS9!}MLH-uE0jShFE((}U z)G{_E92!iuZ*E4ufPWV+&6$(}DcW$-_j8PStXZ`Mz+d`4D@wZT(uWc_&%RT#_*R3+ z!qt1>XJ+;)zY`JJyawac+_r?AKf{pEa>)=UP^ofgK`m-Ljd%gA1n z&=gf|fkAmAc9x^t5p$6PU>fg3pMpcCj8&%?f>)k89BdB)tx}hN2_1cE&bVijUt$oX z8qI9;riLU9FqV-f*_P@P&C)fvh0O*0ltRmMbdsEoZ**O*@L+b`_I95GKt968)Hd}L z!a#^YPu8?mfe=-2f_0P#y`Svx;0H>$jtGNwG&-`%?s?L1yi0Y;szJiD$Hw{kNbqu; z_`FB=$)k7W4G8r4{0rWjNyBL!O)?Mo5A>&>fS=coFTrIxCe7rrZ&QTGLNpDH9hrYJ zz_P!@6>xr?I^>+8IWsxsKF(Y1w~z8O{LN@1vysIzC1_SZEv_8tbI*HfB2ziU(QnAl+tK=o+K{zb+@s0_|X{IW{iBBCo z6Xay@eczAnQA8S==k%$D=-k0K4$)3!X)xWda`&jy556JH@ByuymBIm!n7vB5?; z+a+C~gRVO|?vK=_3h$nhuSsyhI7(W6{Rt^bkwST|r0L`lznYAyx|80nnnVhNhk6`?)9L>U7y(1q;;BNSsd+F;23hjVQ!3EOE&hJA^cV+0&K{ z0(UaoYKIlw(06kt!MPoNp)wmqr#ouE9(KiSseVjlaiXr#FvncXVnlJWN|P>scV7;y zc2@QHgTHe(l@g5rW+n_a}gRAAQq+d05-#AMJ4C z3Rj_XdWzL4bI&pr#sRqYCK6#G^&X5}y#;Mp+8Ejd{Q8ml`IcMjs! zMKV(Kx2XCE`oMbrACCO0@k31bC#|_@31|n|E-%Hdvogg+R6@bbsDFE9|5bv&R$oHN zTRGOUM_{(}wEhbujlB5SpxfF?^w|LMnj?NhM=`>iL3!*?XY!=qj*sad&U_O#+Ob&w zDLUvl>vvY)fMXYY4YXWpu8@bOp7j~0F|5jzU841Nk5i7ClZ{3z{oBR5l*pXGk&>{s z=gX(J=%}lIJZ(4$5->d;?b%a9y4v9D zM27~+ngI`u!&8x$KAu?#;*O8_tPKh zJ|zZwtP+m5I}Qg}MT-mh?nfS1&KWG2T6HG1yNpoG77KiyX;0ENCpJP1j%uc~cB8s~ zAC?xRJ7om~Bi*i0OEd0U!X*vAhI-r=UoZvFwp^O=kR;4M;8=3_dcgI^vghbKZNW*S zv9>k7BvSE9Ar|&Ku3ktJ(4Y2B%K~E6QdaJ7I=E>*NiB)x^qQuRKe#WdmOz64k@YPh7 z>qOv}BH?lhb#Et5L~Yh&Z|rRl>X0f08x#i7^$(WYm?d{e;E!Knyw7_Tt-35)c#EcU zOik9?_qDwwFR}Bk7ks2>sHXcAs%=fTJB5-}t{G}>xV!)DrWVuT$I5zkIR$?gO)6}| zd~5!N)rarf>{(}LrytwP`sP|@@Ijdy6uL_Q z50KdJ-)=5?s#31Y*fganub$Kf6+W@_msoqFMH_-7t9}a(JoRYV#T~e1@Pn{OT=T;* z6~B{e*CFYk5eC+&&phE - - - Exe - netcoreapp2.0 - - - - - - - - - - - - - - - - - PreserveNewest - - - - diff --git a/src/IpcServiceSample.ConsoleServer/Program.cs b/src/IpcServiceSample.ConsoleServer/Program.cs deleted file mode 100644 index 3025610..0000000 --- a/src/IpcServiceSample.ConsoleServer/Program.cs +++ /dev/null @@ -1,61 +0,0 @@ -using IpcServiceSample.ServiceContracts; -using IpcServiceSample.ServiceContracts.Helpers; -using JKang.IpcServiceFramework; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; - -namespace IpcServiceSample.ConsoleServer -{ - class Program - { - static void Main(string[] args) - { - // configure DI - IServiceCollection services = ConfigureServices(new ServiceCollection()); - - // build and run service host - IIpcServiceHost host = new IpcServiceHostBuilder(services.BuildServiceProvider()) - .AddNamedPipeEndpoint("computingEndpoint", "pipeName") - .AddTcpEndpoint("systemEndpoint", IPAddress.Loopback, 45684) - .AddTcpEndpoint("secureEndpoint", IPAddress.Loopback, 44384, new X509Certificate2(@"Certificates\server.pfx", "password")) - .AddTcpEndpoint("xorTranslatedEndpoint", IPAddress.Loopback, 45454, s => new XorStream(s)) - .Build(); - - var source = new CancellationTokenSource(); - Task.WaitAll(host.RunAsync(source.Token), Task.Run(() => - { - Console.WriteLine("Press any key to shutdown."); - Console.ReadKey(); - source.Cancel(); - })); - - Console.WriteLine("Server stopped."); - } - - private static IServiceCollection ConfigureServices(IServiceCollection services) - { - return services - .AddLogging(builder => - { - builder.AddConsole(); - builder.SetMinimumLevel(LogLevel.Debug); - }) - .AddIpc(builder => - { - builder - .AddNamedPipe(options => - { - options.ThreadCount = 2; - }) - .AddService() - .AddService() - .AddService(); - }); - } - } -} diff --git a/src/JKang.IpcServiceFramework.Core/IpcRequest.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Core/IpcRequest.cs rename to src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs diff --git a/src/JKang.IpcServiceFramework.Core/IpcResponse.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs similarity index 91% rename from src/JKang.IpcServiceFramework.Core/IpcResponse.cs rename to src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs index d7197ba..a60c4ff 100644 --- a/src/JKang.IpcServiceFramework.Core/IpcResponse.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs @@ -1,13 +1,11 @@ using System; using System.Reflection; -using Newtonsoft.Json; namespace JKang.IpcServiceFramework { public class IpcResponse { - [JsonConstructor] - private IpcResponse(bool succeed, object data, string failure, string failureDetails, bool userCodeFailure) + public IpcResponse(bool succeed, object data, string failure, string failureDetails, bool userCodeFailure) { Succeed = succeed; Data = data; @@ -64,7 +62,7 @@ public Exception GetException() private static string GetFirstUsableMessage(Exception ex) { - var e = ex; + Exception e = ex; while (e != null) { diff --git a/src/JKang.IpcServiceFramework.Core/IpcServerException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs similarity index 94% rename from src/JKang.IpcServiceFramework.Core/IpcServerException.cs rename to src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs index ba18b3f..c786e48 100644 --- a/src/JKang.IpcServiceFramework.Core/IpcServerException.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs @@ -1,63 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using System.Text; - -namespace JKang.IpcServiceFramework -{ - /// - /// An exception that originated at the server. - /// - [Serializable] - public class IpcServerException : InvalidOperationException - { - public const string ServerFailureDetails = "Server failure details:"; - public string FailureDetails { get; } - - public IpcServerException() - { - } - - public IpcServerException(string message, string failureDetails) - : base(message) - { - FailureDetails = failureDetails; - } - - public IpcServerException(string message, Exception inner) - : base(message, inner) - { - } - - protected IpcServerException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - FailureDetails = info.GetString("FailureDetails"); - } - - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - - info.AddValue("FailureDetails", FailureDetails, typeof(string)); - } - - public override string ToString() - { - if (!string.IsNullOrWhiteSpace(FailureDetails)) - { - var sb = new StringBuilder(); - sb.AppendLine(base.ToString()); - sb.AppendLine(); - sb.AppendLine(ServerFailureDetails); - sb.Append(FailureDetails); - return sb.ToString(); - } - - return base.ToString(); - } - } -} +using System; +using System.Runtime.Serialization; +using System.Text; + +namespace JKang.IpcServiceFramework +{ + /// + /// An exception that originated at the server. + /// + [Serializable] + public class IpcServerException : InvalidOperationException + { + public const string ServerFailureDetails = "Server failure details:"; + public string FailureDetails { get; } + + public IpcServerException() + { + } + + public IpcServerException(string message, string failureDetails) + : base(message) + { + FailureDetails = failureDetails; + } + + public IpcServerException(string message, Exception inner) + : base(message, inner) + { + } + + protected IpcServerException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + FailureDetails = info.GetString("FailureDetails"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + + info.AddValue("FailureDetails", FailureDetails, typeof(string)); + } + + public override string ToString() + { + if (!string.IsNullOrWhiteSpace(FailureDetails)) + { + var sb = new StringBuilder(); + sb.AppendLine(base.ToString()); + sb.AppendLine(); + sb.AppendLine(ServerFailureDetails); + sb.Append(FailureDetails); + return sb.ToString(); + } + + return base.ToString(); + } + } +} diff --git a/src/JKang.IpcServiceFramework.Core/IpcServerUserCodeException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs similarity index 96% rename from src/JKang.IpcServiceFramework.Core/IpcServerUserCodeException.cs rename to src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs index e14d696..98ec87a 100644 --- a/src/JKang.IpcServiceFramework.Core/IpcServerUserCodeException.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs @@ -1,31 +1,31 @@ -using System; -using System.Runtime.Serialization; - -namespace JKang.IpcServiceFramework -{ - /// - /// An exception that originated at the server, in user code. - /// - [Serializable] - public class IpcServerUserCodeException : IpcServerException - { - public IpcServerUserCodeException() - { - } - - public IpcServerUserCodeException(string message, string failureDetails) - : base(message, failureDetails) - { - } - - public IpcServerUserCodeException(string message, Exception inner) - : base(message, inner) - { - } - - protected IpcServerUserCodeException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} +using System; +using System.Runtime.Serialization; + +namespace JKang.IpcServiceFramework +{ + /// + /// An exception that originated at the server, in user code. + /// + [Serializable] + public class IpcServerUserCodeException : IpcServerException + { + public IpcServerUserCodeException() + { + } + + public IpcServerUserCodeException(string message, string failureDetails) + : base(message, failureDetails) + { + } + + public IpcServerUserCodeException(string message, Exception inner) + : base(message, inner) + { + } + + protected IpcServerUserCodeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj b/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj new file mode 100644 index 0000000..67943b7 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + JKang.IpcServiceFramework + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/JKang.IpcServiceFramework.Core/Services/IIpcMessageSerializer.cs b/src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs similarity index 85% rename from src/JKang.IpcServiceFramework.Core/Services/IIpcMessageSerializer.cs rename to src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs index 481d8c4..c3f9a18 100644 --- a/src/JKang.IpcServiceFramework.Core/Services/IIpcMessageSerializer.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs @@ -1,4 +1,4 @@ -namespace JKang.IpcServiceFramework +namespace JKang.IpcServiceFramework.Services { public interface IIpcMessageSerializer { diff --git a/src/JKang.IpcServiceFramework.Core/Services/IValueConverter.cs b/src/JKang.IpcServiceFramework.Abstractions/Services/IValueConverter.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Core/Services/IValueConverter.cs rename to src/JKang.IpcServiceFramework.Abstractions/Services/IValueConverter.cs diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs b/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs new file mode 100644 index 0000000..1874024 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; + +namespace JKang.IpcServiceFramework.Client +{ + public interface IIpcClient + where TInterface : class + { + Task InvokeAsync( + Expression> exp, + CancellationToken cancellationToken = default); + + Task InvokeAsync( + Expression> exp, + CancellationToken cancellationToken = default); + + Task InvokeAsync( + Expression> exp, + CancellationToken cancellationToken = default); + + Task InvokeAsync( + Expression>> exp, + CancellationToken cancellationToken = default); + } +} diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj b/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj new file mode 100644 index 0000000..757361c --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + JKang.IpcServiceFramework.Client + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/JKang.IpcServiceFramework.Client.NamedPipe.csproj b/src/JKang.IpcServiceFramework.Client.NamedPipe/JKang.IpcServiceFramework.Client.NamedPipe.csproj new file mode 100644 index 0000000..bb2d870 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/JKang.IpcServiceFramework.Client.NamedPipe.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Client/NamedPipe/NamedPipeIpcServiceClient.cs b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs similarity index 71% rename from src/JKang.IpcServiceFramework.Client/NamedPipe/NamedPipeIpcServiceClient.cs rename to src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs index 0c21ecf..07ef51f 100644 --- a/src/JKang.IpcServiceFramework.Client/NamedPipe/NamedPipeIpcServiceClient.cs +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs @@ -1,18 +1,17 @@ using JKang.IpcServiceFramework.Services; -using System; using System.IO; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; -namespace JKang.IpcServiceFramework.NamedPipe +namespace JKang.IpcServiceFramework.Client.NamedPipe { - internal class NamedPipeIpcServiceClient : IpcServiceClient + internal class NamedPipeIpcClient : IpcClient where TInterface : class { private readonly string _pipeName; - public NamedPipeIpcServiceClient(IIpcMessageSerializer serializer, IValueConverter converter, string pipeName) + public NamedPipeIpcClient(IIpcMessageSerializer serializer, IValueConverter converter, string pipeName) : base(serializer, converter) { _pipeName = pipeName; diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs new file mode 100644 index 0000000..f51ec4c --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs @@ -0,0 +1,17 @@ +using JKang.IpcServiceFramework.Client.NamedPipe; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class NamedPipeIpcClientServiceCollectionExtensions + { + public static IServiceCollection AddNamedPipeIpcClient( + this IServiceCollection services, string pipeName) + where TContract : class + { + return services.AddIpcClient((serializer, valueConverter) => + { + return new NamedPipeIpcClient(serializer, valueConverter, pipeName); + }); + } + } +} diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/JKang.IpcServiceFramework.Client.Tcp.csproj b/src/JKang.IpcServiceFramework.Client.Tcp/JKang.IpcServiceFramework.Client.Tcp.csproj new file mode 100644 index 0000000..bb2d870 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.Tcp/JKang.IpcServiceFramework.Client.Tcp.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs new file mode 100644 index 0000000..8b72e7e --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs @@ -0,0 +1,109 @@ +using JKang.IpcServiceFramework.Services; +using System; +using System.IO; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace JKang.IpcServiceFramework.Client.Tcp +{ + internal class TcpIpcClient : IpcClient, IDisposable + where TInterface : class + { + private readonly TcpIpcClientOptions _options; + private readonly TcpClient _client = new TcpClient(); + private bool _isDisposed; + + public TcpIpcClient( + IIpcMessageSerializer serializer, + IValueConverter converter, + IPAddress serverIp, + int serverPort) + : this(serializer, converter, new TcpIpcClientOptions + { + ServerIp = serverIp ?? throw new ArgumentNullException(nameof(serverIp)), + ServerPort = serverPort, + }) + { } + + public TcpIpcClient( + IIpcMessageSerializer serializer, + IValueConverter converter, + TcpIpcClientOptions options) + : base(serializer, converter) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + } + + protected override async Task ConnectToServerAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await _client.ConnectAsync(_options.ServerIp, _options.ServerPort).ConfigureAwait(false); + + //IAsyncResult result = _client.BeginConnect(_options.ServerIp, _options.ServerPort, null, null); + + //await Task.Run(() => + //{ + // // poll every 100ms to check cancellation request + // while (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(100), false)) + // { + // if (cancellationToken.IsCancellationRequested) + // { + // _client.EndConnect(result); + // cancellationToken.ThrowIfCancellationRequested(); + // } + // } + //}).ConfigureAwait(false); + + //cancellationToken.Register(() => + //{ + // _client.Close(); + //}); + + Stream stream = _client.GetStream(); + + // if SSL is enabled, wrap the stream in an SslStream in client mode + if (_options.EnableSsl) + { + SslStream ssl; + if (_options.SslValidationCallback == null) + { + ssl = new SslStream(stream, false); + } + else + { + ssl = new SslStream(stream, false, _options.SslValidationCallback); + } + + // set client mode and specify the common name(CN) of the server + if (_options.SslServerIdentity != null) + { + ssl.AuthenticateAsClient(_options.SslServerIdentity); + } + stream = ssl; + } + + return stream; + } + + public void Dispose() => Dispose(true); + + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + + if (disposing) + { + _client.Dispose(); + } + + _isDisposed = true; + } + } +} diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientOptions.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientOptions.cs new file mode 100644 index 0000000..c266624 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientOptions.cs @@ -0,0 +1,14 @@ +using System.Net; +using System.Net.Security; + +namespace JKang.IpcServiceFramework.Client.Tcp +{ + public class TcpIpcClientOptions + { + public IPAddress ServerIp { get; set; } + public int ServerPort { get; set; } + public bool EnableSsl { get; set; } + public string SslServerIdentity { get; set; } + public RemoteCertificateValidationCallback SslValidationCallback { get; set; } + } +} diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs new file mode 100644 index 0000000..959cac6 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs @@ -0,0 +1,28 @@ +using JKang.IpcServiceFramework.Client.Tcp; +using System.Net; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class TcpIpcClientServiceCollectionExtensions + { + public static IServiceCollection AddTcpIpcClient( + this IServiceCollection services, IPAddress serverIp, int serverPort) + where TContract : class + { + return services.AddIpcClient((serializer, valueConverter) => + { + return new TcpIpcClient(serializer, valueConverter, serverIp, serverPort); + }); + } + + public static IServiceCollection AddTcpIpcClient( + this IServiceCollection services, TcpIpcClientOptions options) + where TContract : class + { + return services.AddIpcClient((serializer, valueConverter) => + { + return new TcpIpcClient(serializer, valueConverter, options); + }); + } + } +} diff --git a/src/JKang.IpcServiceFramework.Client/IpcServiceClient.cs b/src/JKang.IpcServiceFramework.Client/IpcClient.cs similarity index 89% rename from src/JKang.IpcServiceFramework.Client/IpcServiceClient.cs rename to src/JKang.IpcServiceFramework.Client/IpcClient.cs index df6381b..a098080 100644 --- a/src/JKang.IpcServiceFramework.Client/IpcServiceClient.cs +++ b/src/JKang.IpcServiceFramework.Client/IpcClient.cs @@ -8,16 +8,16 @@ using System.Threading; using System.Threading.Tasks; -namespace JKang.IpcServiceFramework +namespace JKang.IpcServiceFramework.Client { - public abstract class IpcServiceClient + public abstract class IpcClient: IIpcClient where TInterface : class { private static readonly ProxyGenerator _proxyGenerator = new ProxyGenerator(); private readonly IIpcMessageSerializer _serializer; private readonly IValueConverter _converter; - protected IpcServiceClient( + protected IpcClient( IIpcMessageSerializer serializer, IValueConverter converter) { @@ -26,7 +26,7 @@ protected IpcServiceClient( } public async Task InvokeAsync(Expression> exp, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { IpcRequest request = GetRequest(exp, new MyInterceptor()); IpcResponse response = await GetResponseAsync(request, cancellationToken).ConfigureAwait(false); @@ -42,7 +42,7 @@ public async Task InvokeAsync(Expression> exp, } public async Task InvokeAsync(Expression> exp, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { IpcRequest request = GetRequest(exp, new MyInterceptor()); IpcResponse response = await GetResponseAsync(request, cancellationToken).ConfigureAwait(false); @@ -65,7 +65,7 @@ public async Task InvokeAsync(Expression> exp, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { IpcRequest request = GetRequest(exp, new MyInterceptor()); IpcResponse response = await GetResponseAsync(request, cancellationToken).ConfigureAwait(false); @@ -81,7 +81,7 @@ public async Task InvokeAsync(Expression> exp, } public async Task InvokeAsync(Expression>> exp, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { IpcRequest request = GetRequest(exp, new MyInterceptor>()); IpcResponse response = await GetResponseAsync(request, cancellationToken).ConfigureAwait(false); @@ -103,7 +103,6 @@ public async Task InvokeAsync(Expression GetResponseAsync(IpcRequest request, CancellationToken cancellationToken) { using (Stream client = await ConnectToServerAsync(cancellationToken).ConfigureAwait(false)) - using (var writer = new IpcWriter(client, _serializer, leaveOpen: true)) - using (var reader = new IpcReader(client, _serializer, leaveOpen: true)) + using (Stream client2 = TransformStream(client)) + using (var writer = new IpcWriter(client2, _serializer, leaveOpen: true)) + using (var reader = new IpcReader(client2, _serializer, leaveOpen: true)) { // send request await writer.WriteAsync(request, cancellationToken).ConfigureAwait(false); @@ -150,6 +150,8 @@ private async Task GetResponseAsync(IpcRequest request, Cancellatio } } + protected virtual Stream TransformStream(Stream input) => input; + private class MyInterceptor : IInterceptor { public IInvocation LastInvocation { get; private set; } diff --git a/src/JKang.IpcServiceFramework.Client/IpcClientServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Client/IpcClientServiceCollectionExtensions.cs new file mode 100644 index 0000000..67b02bf --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client/IpcClientServiceCollectionExtensions.cs @@ -0,0 +1,32 @@ +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Services; +using System; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class IpcClientServiceCollectionExtensions + { + public static IServiceCollection AddIpcClient(this IServiceCollection services, + Func> factory) + where TContract : class + { + if (factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } + + services + .TryAddIpcInternalServices() + ; + + services.AddScoped(serviceProvider => + { + IIpcMessageSerializer serializer = serviceProvider.GetRequiredService(); + IValueConverter valueConverter = serviceProvider.GetRequiredService(); + return factory(serializer, valueConverter); + }); + + return services; + } + } +} diff --git a/src/JKang.IpcServiceFramework.Client/IpcServiceClientBuilder.cs b/src/JKang.IpcServiceFramework.Client/IpcServiceClientBuilder.cs deleted file mode 100644 index bc58ec9..0000000 --- a/src/JKang.IpcServiceFramework.Client/IpcServiceClientBuilder.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using JKang.IpcServiceFramework.Services; - -namespace JKang.IpcServiceFramework -{ - public class IpcServiceClientBuilder - where TInterface : class - { - private IIpcMessageSerializer _serializer = new DefaultIpcMessageSerializer(); - private IValueConverter _valueConverter = new DefaultValueConverter(); - private Func> _factory = null; - - public IpcServiceClientBuilder WithIpcMessageSerializer(IIpcMessageSerializer serializer) - { - _serializer = serializer; - return this; - } - - public IpcServiceClientBuilder WithValueConverter(IValueConverter valueConverter) - { - _valueConverter = valueConverter; - return this; - } - - public IpcServiceClientBuilder SetFactory(Func> factory) - { - _factory = factory; - return this; - } - - public IpcServiceClient Build() - { - if (_factory == null) - { - throw new InvalidOperationException("Client factory is not set."); - } - - return _factory(_serializer, _valueConverter); - } - } -} diff --git a/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj b/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj index 2cbc182..d8318cc 100644 --- a/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj +++ b/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj @@ -7,10 +7,11 @@ - + + diff --git a/src/JKang.IpcServiceFramework.Client/NamedPipe/NamedPipeIpcServiceClientBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Client/NamedPipe/NamedPipeIpcServiceClientBuilderExtensions.cs deleted file mode 100644 index 9f38ce7..0000000 --- a/src/JKang.IpcServiceFramework.Client/NamedPipe/NamedPipeIpcServiceClientBuilderExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JKang.IpcServiceFramework.NamedPipe; - -namespace JKang.IpcServiceFramework -{ - public static class NamedPipeIpcServiceClientBuilderExtensions - { - public static IpcServiceClientBuilder UseNamedPipe( - this IpcServiceClientBuilder builder, string pipeName) - where TInterface : class - { - builder.SetFactory((serializer, converter) => new NamedPipeIpcServiceClient(serializer, converter, pipeName)); - - return builder; - } - } -} diff --git a/src/JKang.IpcServiceFramework.Client/Tcp/TcpIpcServiceClient.cs b/src/JKang.IpcServiceFramework.Client/Tcp/TcpIpcServiceClient.cs deleted file mode 100644 index b21744b..0000000 --- a/src/JKang.IpcServiceFramework.Client/Tcp/TcpIpcServiceClient.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using JKang.IpcServiceFramework.Services; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; -using System.Threading; -using System.Net.Security; - -namespace JKang.IpcServiceFramework.Tcp -{ - internal class TcpIpcServiceClient : IpcServiceClient - where TInterface : class - { - private readonly IPAddress _serverIp; - private readonly Int32 _serverPort; - private readonly Func _streamTranslator; - private readonly string _sslServerIdentity; - private readonly RemoteCertificateValidationCallback _sslValidationCallback; - - - public bool SSL { get; private set; } - - public TcpIpcServiceClient(IIpcMessageSerializer serializer, IValueConverter converter, IPAddress serverIp, int serverPort) - : base(serializer, converter) - { - _serverIp = serverIp; - _serverPort = serverPort; - SSL = false; - } - - public TcpIpcServiceClient(IIpcMessageSerializer serializer, IValueConverter converter, IPAddress serverIp, int serverPort, Func streamTranslator) - : this(serializer, converter, serverIp, serverPort) - { - _streamTranslator = streamTranslator; - } - - public TcpIpcServiceClient(IIpcMessageSerializer serializer, IValueConverter converter, IPAddress serverIp, int serverPort, string sslServerIdentity, RemoteCertificateValidationCallback sslCertificateValidationCallback) - : this(serializer, converter, serverIp, serverPort) - { - _sslValidationCallback = sslCertificateValidationCallback; - _sslServerIdentity = sslServerIdentity; - SSL = true; - } - - public TcpIpcServiceClient(IIpcMessageSerializer serializer, IValueConverter converter, IPAddress serverIp, int serverPort, string sslServerIdentity) - : this(serializer, converter, serverIp, serverPort, sslServerIdentity, (RemoteCertificateValidationCallback)null) - { - } - - public TcpIpcServiceClient(IIpcMessageSerializer serializer, IValueConverter converter, IPAddress serverIp, int serverPort, string sslServerIdentity, Func streamTranslator) - : this(serializer, converter, serverIp, serverPort, sslServerIdentity, (RemoteCertificateValidationCallback)null) - { - _streamTranslator = streamTranslator; - } - - public TcpIpcServiceClient(IIpcMessageSerializer serializer, IValueConverter converter, IPAddress serverIp, int serverPort, string sslServerIdentity, RemoteCertificateValidationCallback sslCertificateValidationCallback, Func streamTranslator) - : this(serializer, converter, serverIp, serverPort, sslServerIdentity, sslCertificateValidationCallback) - { - _streamTranslator = streamTranslator; - } - - protected override async Task ConnectToServerAsync(CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var client = new TcpClient(); - IAsyncResult result = client.BeginConnect(_serverIp, _serverPort, null, null); - - await Task.Run(() => - { - // poll every 100ms to check cancellation request - while (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(100), false)) - { - if (cancellationToken.IsCancellationRequested) - { - client.EndConnect(result); - cancellationToken.ThrowIfCancellationRequested(); - } - } - }).ConfigureAwait(false); - - cancellationToken.Register(() => - { - client.Close(); - }); - - Stream stream = client.GetStream(); - - // if there's a stream translator, apply it here - if (_streamTranslator != null) - { - stream = _streamTranslator(stream); - } - - // if SSL is enabled, wrap the stream in an SslStream in client mode - if (SSL) - { - SslStream ssl; - if (_sslValidationCallback == null) - { - ssl = new SslStream(stream, false); - } - else - { - ssl = new SslStream(stream, false, _sslValidationCallback); - } - - // set client mode and specify the common name(CN) of the server - ssl.AuthenticateAsClient(_sslServerIdentity); - stream = ssl; - } - - return stream; - } - } -} diff --git a/src/JKang.IpcServiceFramework.Client/Tcp/TcpIpcServiceClientBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Client/Tcp/TcpIpcServiceClientBuilderExtensions.cs deleted file mode 100644 index a28c656..0000000 --- a/src/JKang.IpcServiceFramework.Client/Tcp/TcpIpcServiceClientBuilderExtensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Net.Security; -using JKang.IpcServiceFramework.Tcp; - -namespace JKang.IpcServiceFramework -{ - public static class TcpIpcServiceClientBuilderExtensions - { - public static IpcServiceClientBuilder UseTcp( - this IpcServiceClientBuilder builder, IPAddress serverIp, int serverPort) - where TInterface : class - { - builder.SetFactory((serializer, converter) => new TcpIpcServiceClient(serializer, converter, serverIp, serverPort)); - - return builder; - } - - public static IpcServiceClientBuilder UseTcp( - this IpcServiceClientBuilder builder, IPAddress serverIp, int serverPort, - Func streamTranslator) - where TInterface : class - { - builder.SetFactory((serializer, converter) => new TcpIpcServiceClient(serializer, converter, serverIp, serverPort, streamTranslator)); - - return builder; - } - - public static IpcServiceClientBuilder UseTcp( - this IpcServiceClientBuilder builder, IPAddress serverIp, int serverPort, - string sslServerIdentity) - where TInterface : class - { - builder.SetFactory((serializer, converter) => new TcpIpcServiceClient(serializer, converter, serverIp, serverPort, sslServerIdentity)); - - return builder; - } - - public static IpcServiceClientBuilder UseTcp( - this IpcServiceClientBuilder builder, IPAddress serverIp, int serverPort, - string sslServerIdentity, RemoteCertificateValidationCallback sslCertificateValidationCallback) - where TInterface : class - { - builder.SetFactory((serializer, converter) => new TcpIpcServiceClient(serializer, converter, serverIp, serverPort, sslServerIdentity, sslCertificateValidationCallback)); - - return builder; - } - - public static IpcServiceClientBuilder UseTcp( - this IpcServiceClientBuilder builder, IPAddress serverIp, int serverPort, - string sslServerIdentity, Func streamTranslator) - where TInterface : class - { - builder.SetFactory((serializer, converter) => new TcpIpcServiceClient(serializer, converter, serverIp, serverPort, sslServerIdentity, streamTranslator)); - - return builder; - } - - public static IpcServiceClientBuilder UseTcp( - this IpcServiceClientBuilder builder, IPAddress serverIp, int serverPort, - string sslServerIdentity, RemoteCertificateValidationCallback sslCertificateValidationCallback, Func streamTranslator) - where TInterface : class - { - builder.SetFactory((serializer, converter) => new TcpIpcServiceClient(serializer, converter, serverIp, serverPort, sslServerIdentity, sslCertificateValidationCallback, streamTranslator)); - - return builder; - } - } -} diff --git a/src/JKang.IpcServiceFramework.Core.Tests/DefaultValueConverterTest.cs b/src/JKang.IpcServiceFramework.Core.Tests/DefaultValueConverterTest.cs index 051cc59..20e291a 100644 --- a/src/JKang.IpcServiceFramework.Core.Tests/DefaultValueConverterTest.cs +++ b/src/JKang.IpcServiceFramework.Core.Tests/DefaultValueConverterTest.cs @@ -1,197 +1,140 @@ +using AutoFixture.Xunit2; +using JKang.IpcServiceFramework.Core.Tests.Fixtures; using JKang.IpcServiceFramework.Services; -using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using System; using System.Collections.Generic; +using Xunit; namespace JKang.IpcServiceFramework.Core.Tests { - [TestClass] public class DefaultValueConverterTest { - private DefaultValueConverter _sut; + private readonly DefaultValueConverter _sut = new DefaultValueConverter(); - [TestInitialize] - public void Init() + [Theory, AutoData] + public void TryConvert_FloatToDouble(float expected) { - _sut = new DefaultValueConverter(); - } - - [TestMethod] - public void TryConvert_FloatToDouble() - { - float expected = 123.4f; - bool succeed = _sut.TryConvert(expected, typeof(double), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(double)); - Assert.AreEqual((double)expected, actual); + Assert.True(succeed); + Assert.IsType(actual); + Assert.Equal((double)expected, actual); } - [TestMethod] - public void TryConvert_Int32ToInt64() + [Theory, AutoData] + public void TryConvert_Int32ToInt64(int expected) { - int expected = 123; - bool succeed = _sut.TryConvert(expected, typeof(long), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(long)); - Assert.AreEqual((long)expected, actual); + Assert.True(succeed); + Assert.IsType(actual); + Assert.Equal((long)expected, actual); } - [TestMethod] - public void TryConvert_SameType() + [Theory, AutoData] + public void TryConvert_SameType(DateTime expected) { - DateTime expected = DateTime.UtcNow; - bool succeed = _sut.TryConvert(expected, typeof(DateTime), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(DateTime)); - Assert.AreEqual(expected, actual); + Assert.True(succeed); + Assert.IsType(actual); + Assert.Equal(expected, actual); } - [TestMethod] - public void TryConvert_JObjectToComplexType() + [Theory, AutoData] + public void TryConvert_JObjectToComplexType(ComplexType expected) { - var expected = new ComplexType - { - Int32Value = 123, - StringValue = "hello" - }; object jObj = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(expected)); bool succeed = _sut.TryConvert(jObj, typeof(ComplexType), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(ComplexType)); - Assert.AreEqual(expected.Int32Value, ((ComplexType)actual).Int32Value); - Assert.AreEqual(expected.StringValue, ((ComplexType)actual).StringValue); + Assert.True(succeed); + Assert.IsType(actual); + Assert.Equal(expected.Int32Value, ((ComplexType)actual).Int32Value); + Assert.Equal(expected.StringValue, ((ComplexType)actual).StringValue); } - [TestMethod] - public void TryConvert_Int32Array() + [Theory, AutoData] + public void TryConvert_Int32Array(int[] expected) { - int[] expected = new[] { 1, 2 }; object jObj = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(expected)); bool succeed = _sut.TryConvert(jObj, typeof(int[]), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(int[])); - var actualArray = actual as int[]; - Assert.AreEqual(expected.Length, actualArray.Length); - for (int i = 0; i < expected.Length; i++) - { - Assert.AreEqual(expected[i], actualArray[i]); - } + Assert.True(succeed); + Assert.IsType(actual); + Assert.Equal(expected, actual as int[]); } - [TestMethod] - public void TryConvert_Int32List() + [Theory, AutoData] + public void TryConvert_Int32List(List expected) { - var expected = new List { 1, 2 }; object jObj = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(expected)); bool succeed = _sut.TryConvert(jObj, typeof(List), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(List)); - var actualList = actual as List; - Assert.AreEqual(expected.Count, actualList.Count); - for (int i = 0; i < expected.Count; i++) - { - Assert.AreEqual(expected[i], actualList[i]); - } + Assert.True(succeed); + Assert.IsType>(actual); + Assert.Equal(expected, actual as List); } - [TestMethod] - public void TryConvert_ComplexTypeArray() + [Theory, AutoData] + public void TryConvert_ComplexTypeArray(ComplexType[] expected) { - ComplexType[] expected = new[] - { - new ComplexType { Int32Value = 123, StringValue = "abc" }, - new ComplexType { Int32Value = 456, StringValue = "edf" }, - }; object jObj = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(expected)); bool succeed = _sut.TryConvert(jObj, typeof(ComplexType[]), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(ComplexType[])); + Assert.True(succeed); + Assert.IsType(actual); var actualArray = actual as ComplexType[]; - Assert.AreEqual(expected.Length, actualArray.Length); + Assert.Equal(expected.Length, actualArray.Length); for (int i = 0; i < expected.Length; i++) { - Assert.IsNotNull(actualArray[i]); - Assert.AreEqual(expected[i].Int32Value, actualArray[i].Int32Value); - Assert.AreEqual(expected[i].StringValue, actualArray[i].StringValue); + Assert.NotNull(actualArray[i]); + Assert.Equal(expected[i].Int32Value, actualArray[i].Int32Value); + Assert.Equal(expected[i].StringValue, actualArray[i].StringValue); } } - [TestMethod] + [Fact] public void TryConvert_DerivedTypeToBaseType() { bool succeed = _sut.TryConvert(new ComplexType(), typeof(IComplexType), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(ComplexType)); + Assert.True(succeed); + Assert.IsType(actual); } - [TestMethod] - public void TryConvert_StringToEnum() + [Theory, AutoData] + public void TryConvert_StringToEnum(EnumType expected) { - EnumType expected = EnumType.SecondOption; - bool succeed = _sut.TryConvert(expected.ToString(), typeof(EnumType), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(EnumType)); - Assert.AreEqual(expected, actual); + Assert.True(succeed); + Assert.IsType(actual); + Assert.Equal(expected, actual); } - [TestMethod] - public void TryConvert_Int32ToEnum() + [Theory, AutoData] + public void TryConvert_Int32ToEnum(EnumType expected) { - EnumType expected = EnumType.SecondOption; - bool succeed = _sut.TryConvert((int)expected, typeof(EnumType), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(EnumType)); - Assert.AreEqual(expected, actual); + Assert.True(succeed); + Assert.IsType(actual); + Assert.Equal(expected, actual); } - [TestMethod] - public void TryConvert_StringToGuid() + [Theory, AutoData] + public void TryConvert_StringToGuid(Guid expected) { - var expected = Guid.NewGuid(); - bool succeed = _sut.TryConvert(expected.ToString(), typeof(Guid), out object actual); - Assert.IsTrue(succeed); - Assert.IsInstanceOfType(actual, typeof(Guid)); - Assert.AreEqual(expected, actual); - } - - interface IComplexType - { - int Int32Value { get; } - string StringValue { get; } - } - - class ComplexType : IComplexType - { - public int Int32Value { get; set; } - public string StringValue { get; set; } - } - - enum EnumType - { - FirstOption, - SecondOption + Assert.True(succeed); + Assert.IsType(actual); + Assert.Equal(expected, actual); } } } diff --git a/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/ComplexType.cs b/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/ComplexType.cs new file mode 100644 index 0000000..646e617 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/ComplexType.cs @@ -0,0 +1,8 @@ +namespace JKang.IpcServiceFramework.Core.Tests.Fixtures +{ + public class ComplexType : IComplexType + { + public int Int32Value { get; set; } + public string StringValue { get; set; } + } +} diff --git a/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/EnumType.cs b/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/EnumType.cs new file mode 100644 index 0000000..e30c0ac --- /dev/null +++ b/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/EnumType.cs @@ -0,0 +1,8 @@ +namespace JKang.IpcServiceFramework.Core.Tests.Fixtures +{ + public enum EnumType + { + FirstOption, + SecondOption + } +} diff --git a/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/IComplexType.cs b/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/IComplexType.cs new file mode 100644 index 0000000..499d6a8 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Core.Tests/Fixtures/IComplexType.cs @@ -0,0 +1,8 @@ +namespace JKang.IpcServiceFramework.Core.Tests.Fixtures +{ + public interface IComplexType + { + int Int32Value { get; } + string StringValue { get; } + } +} diff --git a/src/JKang.IpcServiceFramework.Core.Tests/JKang.IpcServiceFramework.Core.Tests.csproj b/src/JKang.IpcServiceFramework.Core.Tests/JKang.IpcServiceFramework.Core.Tests.csproj index 0db894a..39efbf7 100644 --- a/src/JKang.IpcServiceFramework.Core.Tests/JKang.IpcServiceFramework.Core.Tests.csproj +++ b/src/JKang.IpcServiceFramework.Core.Tests/JKang.IpcServiceFramework.Core.Tests.csproj @@ -1,15 +1,23 @@ - + - netcoreapp2.0 + netcoreapp3.1 false - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/JKang.IpcServiceFramework.Core/IO/IpcReader.cs b/src/JKang.IpcServiceFramework.Core/IO/IpcReader.cs index ffeae66..07ebca5 100644 --- a/src/JKang.IpcServiceFramework.Core/IO/IpcReader.cs +++ b/src/JKang.IpcServiceFramework.Core/IO/IpcReader.cs @@ -1,4 +1,5 @@ -using System; +using JKang.IpcServiceFramework.Services; +using System; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -23,13 +24,13 @@ public IpcReader(Stream stream, IIpcMessageSerializer serializer, bool leaveOpen _leaveOpen = leaveOpen; } - public async Task ReadIpcRequestAsync(CancellationToken cancellationToken = default(CancellationToken)) + public async Task ReadIpcRequestAsync(CancellationToken cancellationToken = default) { byte[] binary = await ReadMessageAsync(cancellationToken).ConfigureAwait(false); return _serializer.DeserializeRequest(binary); } - public async Task ReadIpcResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) + public async Task ReadIpcResponseAsync(CancellationToken cancellationToken = default) { byte[] binary = await ReadMessageAsync(cancellationToken).ConfigureAwait(false); return _serializer.DeserializeResponse(binary); @@ -37,7 +38,9 @@ public IpcReader(Stream stream, IIpcMessageSerializer serializer, bool leaveOpen private async Task ReadMessageAsync(CancellationToken cancellationToken) { - int headerLength = await _stream.ReadAsync(_lengthBuffer, 0, _lengthBuffer.Length, cancellationToken); + int headerLength = await _stream + .ReadAsync(_lengthBuffer, 0, _lengthBuffer.Length, cancellationToken) + .ConfigureAwait(false); if (headerLength != 4) { @@ -53,7 +56,9 @@ private async Task ReadMessageAsync(CancellationToken cancellationToken) { while (totalBytesReceived < expectedLength) { - int dataLength = await _stream.ReadAsync(bytes, 0, remainingBytes, cancellationToken); + int dataLength = await _stream + .ReadAsync(bytes, 0, remainingBytes, cancellationToken) + .ConfigureAwait(false); if (dataLength == 0) { diff --git a/src/JKang.IpcServiceFramework.Core/IO/IpcWriter.cs b/src/JKang.IpcServiceFramework.Core/IO/IpcWriter.cs index 3dea6ee..c652103 100644 --- a/src/JKang.IpcServiceFramework.Core/IO/IpcWriter.cs +++ b/src/JKang.IpcServiceFramework.Core/IO/IpcWriter.cs @@ -1,4 +1,5 @@ -using System; +using JKang.IpcServiceFramework.Services; +using System; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -24,14 +25,14 @@ public IpcWriter(Stream stream, IIpcMessageSerializer serializer, bool leaveOpen } public async Task WriteAsync(IpcRequest request, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { byte[] binary = _serializer.SerializeRequest(request); await WriteMessageAsync(binary, cancellationToken).ConfigureAwait(false); } public async Task WriteAsync(IpcResponse response, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { byte[] binary = _serializer.SerializeResponse(response); await WriteMessageAsync(binary, cancellationToken).ConfigureAwait(false); diff --git a/src/JKang.IpcServiceFramework.Core/InternalServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Core/InternalServiceCollectionExtensions.cs new file mode 100644 index 0000000..eb2390a --- /dev/null +++ b/src/JKang.IpcServiceFramework.Core/InternalServiceCollectionExtensions.cs @@ -0,0 +1,19 @@ +using JKang.IpcServiceFramework.Services; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class InternalServiceCollectionExtensions + { + public static IServiceCollection TryAddIpcInternalServices(this IServiceCollection services) + { + services + .TryAddScoped(); + + services + .TryAddScoped(); + + return services; + } + } +} diff --git a/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj b/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj index 8191552..1b18978 100644 --- a/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj +++ b/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj @@ -7,7 +7,16 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs b/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs index 42984cb..bf43bb3 100644 --- a/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs +++ b/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using System.Text; -namespace JKang.IpcServiceFramework +namespace JKang.IpcServiceFramework.Services { public class DefaultIpcMessageSerializer : IIpcMessageSerializer { @@ -42,4 +42,4 @@ private byte[] Serialize(object obj) return Encoding.UTF8.GetBytes(json); } } -} \ No newline at end of file +} diff --git a/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs b/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs index 92ac242..ab9b4bc 100644 --- a/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs +++ b/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs @@ -8,6 +8,11 @@ public class DefaultValueConverter : IValueConverter { public bool TryConvert(object origValue, Type destType, out object destValue) { + if (destType is null) + { + throw new ArgumentNullException(nameof(destType)); + } + if (origValue == null) { destValue = null; diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs new file mode 100644 index 0000000..c804f3d --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs @@ -0,0 +1,14 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace JKang.IpcServiceFramework.Hosting +{ + public interface IIpcEndpoint + { + string Name { get; } + + Task StartAsync(CancellationToken cancellationToken = default); + + Task StopAsync(CancellationToken cancellationToken = default); + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcHostBuilder.cs b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcHostBuilder.cs new file mode 100644 index 0000000..853325f --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcHostBuilder.cs @@ -0,0 +1,9 @@ +using System; + +namespace JKang.IpcServiceFramework.Hosting +{ + public interface IIpcHostBuilder + { + IIpcHostBuilder AddIpcEndpoint(Func factory); + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs new file mode 100644 index 0000000..d8343e0 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs @@ -0,0 +1,9 @@ +namespace JKang.IpcServiceFramework.Hosting.Abstractions +{ + public abstract class IpcEndpointOptions + { + public int MaxConcurrentCalls { get; set; } = 4; + + public bool IncludeFailureDetailsInResponse { get; set; } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcHostingConfigurationException.cs b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcHostingConfigurationException.cs new file mode 100644 index 0000000..cbbe99d --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcHostingConfigurationException.cs @@ -0,0 +1,18 @@ +using System; + +namespace JKang.IpcServiceFramework.Hosting +{ + public class IpcHostingConfigurationException : Exception + { + public IpcHostingConfigurationException() + { } + + public IpcHostingConfigurationException(string message) + : base(message) + { } + + public IpcHostingConfigurationException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj b/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj new file mode 100644 index 0000000..e6757ff --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + JKang.IpcServiceFramework.Hosting + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/GlobalSuppressions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/GlobalSuppressions.cs new file mode 100644 index 0000000..bc4374e --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Globalization support is not planned.")] diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj new file mode 100644 index 0000000..6e9ab67 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs new file mode 100644 index 0000000..28208b6 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs @@ -0,0 +1,38 @@ +using JKang.IpcServiceFramework.Hosting.NamedPipe; +using JKang.IpcServiceFramework.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; + +namespace JKang.IpcServiceFramework.Hosting +{ + public static class NamedPipeIpcHostBuilderExtensions + { + public static IIpcHostBuilder AddNamedPipeEndpoint(this IIpcHostBuilder builder, + string name, string pipeName) + where TContract : class + { + return builder.AddNamedPipeEndpoint(name, pipeName, null); + } + + public static IIpcHostBuilder AddNamedPipeEndpoint(this IIpcHostBuilder builder, + string name, string pipeName, Action configure) + where TContract : class + { + var options = new NamedPipeIpcServiceEndpointOptions(pipeName); + configure?.Invoke(options); + + builder.AddIpcEndpoint(serviceProvider => + { + IIpcMessageSerializer serializer = serviceProvider.GetRequiredService(); + IValueConverter valueConverter = serviceProvider.GetRequiredService(); + ILogger> logger = serviceProvider + .GetRequiredService>>(); + + return new NamedPipeIpcServiceEndpoint(name, options, serializer, valueConverter, logger, serviceProvider); + }); + + return builder; + } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs new file mode 100644 index 0000000..97fa8dc --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs @@ -0,0 +1,37 @@ +using JKang.IpcServiceFramework.Services; +using Microsoft.Extensions.Logging; +using System; +using System.IO.Pipes; +using System.Threading; +using System.Threading.Tasks; + +namespace JKang.IpcServiceFramework.Hosting.NamedPipe +{ + public class NamedPipeIpcServiceEndpoint : IpcEndpoint + where TContract : class + { + private readonly NamedPipeIpcServiceEndpointOptions _options; + + public NamedPipeIpcServiceEndpoint( + string name, + NamedPipeIpcServiceEndpointOptions options, + IIpcMessageSerializer serializer, + IValueConverter valueConverter, + ILogger> logger, + IServiceProvider serviceProvider) + : base(name, options, serviceProvider, serializer, valueConverter, logger) + { + _options = options; + } + + protected override async Task WaitAndProcessAsync(CancellationToken cancellationToken) + { + using (var server = new NamedPipeServerStream(_options.PipeName, PipeDirection.InOut, _options.MaxConcurrentCalls, + PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) + { + await server.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false); + await ProcessAsync(server, cancellationToken).ConfigureAwait(false); + } + } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpointOptions.cs new file mode 100644 index 0000000..f345dd6 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpointOptions.cs @@ -0,0 +1,14 @@ +using JKang.IpcServiceFramework.Hosting.Abstractions; + +namespace JKang.IpcServiceFramework.Hosting.NamedPipe +{ + public class NamedPipeIpcServiceEndpointOptions : IpcEndpointOptions + { + public NamedPipeIpcServiceEndpointOptions(string pipeName) + { + PipeName = pipeName; + } + + public string PipeName { get; } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/GlobalSuppressions.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/GlobalSuppressions.cs new file mode 100644 index 0000000..bc4374e --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Globalization support is not planned.")] diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/JKang.IpcServiceFramework.Hosting.Tcp.csproj b/src/JKang.IpcServiceFramework.Hosting.Tcp/JKang.IpcServiceFramework.Hosting.Tcp.csproj new file mode 100644 index 0000000..61c2952 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/JKang.IpcServiceFramework.Hosting.Tcp.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs new file mode 100644 index 0000000..e92a678 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs @@ -0,0 +1,53 @@ +using JKang.IpcServiceFramework.Services; +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.Net.Security; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace JKang.IpcServiceFramework.Hosting.Tcp +{ + public class TcpIpcEndpoint : IpcEndpoint + where TContract : class + { + private readonly TcpIpcEndpointOptions _options; + private readonly TcpListener _listener; + + public TcpIpcEndpoint( + string name, + TcpIpcEndpointOptions options, + IIpcMessageSerializer serializer, + IValueConverter valueConverter, + ILogger> logger, + IServiceProvider serviceProvider) + : base(name, options, serviceProvider, serializer, valueConverter, logger) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _listener = new TcpListener(_options.IpEndpoint, _options.Port); + _listener.Start(); + } + + protected override async Task WaitAndProcessAsync(CancellationToken cancellationToken) + { + using (TcpClient client = await _listener.AcceptTcpClientAsync().ConfigureAwait(false)) + { + Stream server = client.GetStream(); + server = TransformStream(server); + + // if SSL is enabled, wrap the stream in an SslStream in client mode + if (_options.EnableSsl) + { + var ssl = new SslStream(server, false); + ssl.AuthenticateAsServer(_options.SslCertificate + ?? throw new IpcHostingConfigurationException("Invalid TCP IPC endpoint configured: SSL enabled without providing certificate.")); + server = ssl; + } + + await ProcessAsync(server, cancellationToken).ConfigureAwait(false); + client.Close(); + } + } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpointOptions.cs new file mode 100644 index 0000000..8d567e5 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpointOptions.cs @@ -0,0 +1,14 @@ +using JKang.IpcServiceFramework.Hosting.Abstractions; +using System.Net; +using System.Security.Cryptography.X509Certificates; + +namespace JKang.IpcServiceFramework.Hosting.Tcp +{ + public class TcpIpcEndpointOptions : IpcEndpointOptions + { + public IPAddress IpEndpoint { get; set; } = IPAddress.Loopback; + public int Port { get; set; } + public bool EnableSsl { get; set; } + public X509Certificate SslCertificate { get; set; } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs new file mode 100644 index 0000000..53fdd7f --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs @@ -0,0 +1,43 @@ +using JKang.IpcServiceFramework.Hosting.Tcp; +using JKang.IpcServiceFramework.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Net; + +namespace JKang.IpcServiceFramework.Hosting +{ + public static class TcpIpcHostBuilderExtensions + { + public static IIpcHostBuilder AddTcpEndpoint(this IIpcHostBuilder builder, + string name, IPAddress ipEndpoint, int port) + where TContract : class + { + return builder.AddTcpEndpoint(name, options => + { + options.IpEndpoint = ipEndpoint; + options.Port = port; + }); + } + + public static IIpcHostBuilder AddTcpEndpoint(this IIpcHostBuilder builder, + string name, Action configure) + where TContract : class + { + var options = new TcpIpcEndpointOptions(); + configure?.Invoke(options); + + builder.AddIpcEndpoint(serviceProvider => + { + IIpcMessageSerializer serializer = serviceProvider.GetRequiredService(); + IValueConverter valueConverter = serviceProvider.GetRequiredService(); + ILogger> logger = serviceProvider + .GetRequiredService>>(); + + return new TcpIpcEndpoint(name, options, serializer, valueConverter, logger, serviceProvider); + }); + + return builder; + } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting/GenericHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Hosting/GenericHostBuilderExtensions.cs new file mode 100644 index 0000000..5a8be0b --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting/GenericHostBuilderExtensions.cs @@ -0,0 +1,15 @@ +using JKang.IpcServiceFramework.Hosting; +using System; + +namespace Microsoft.Extensions.Hosting +{ + public static class GenericHostBuilderExtensions + { + public static IHostBuilder ConfigureIpcHost(this IHostBuilder builder, Action configure) + { + var ipcHostBuilder = new IpcHostBuilder(builder); + configure?.Invoke(ipcHostBuilder); + return builder; + } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting/GlobalSuppressions.cs b/src/JKang.IpcServiceFramework.Hosting/GlobalSuppressions.cs new file mode 100644 index 0000000..bc4374e --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Globalization support is not planned.")] diff --git a/src/JKang.IpcServiceFramework.Server/IpcServiceEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs similarity index 61% rename from src/JKang.IpcServiceFramework.Server/IpcServiceEndpoint.cs rename to src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs index 5dc872e..bee73eb 100644 --- a/src/JKang.IpcServiceFramework.Server/IpcServiceEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs @@ -1,4 +1,5 @@ -using JKang.IpcServiceFramework.IO; +using JKang.IpcServiceFramework.Hosting.Abstractions; +using JKang.IpcServiceFramework.IO; using JKang.IpcServiceFramework.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -9,39 +10,41 @@ using System.Threading; using System.Threading.Tasks; -namespace JKang.IpcServiceFramework +namespace JKang.IpcServiceFramework.Hosting { - public abstract class IpcServiceEndpoint + public abstract class IpcEndpoint : IIpcEndpoint + where TContract : class { - protected IpcServiceEndpoint(string name, IServiceProvider serviceProvider, bool includeFailureDetailsInResponse = false) + private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + private readonly IpcEndpointOptions _options; + private readonly IServiceProvider _serviceProvider; + private readonly IIpcMessageSerializer _serializer; + private readonly IValueConverter _valueConverter; + private readonly ILogger _logger; + + protected IpcEndpoint( + string name, + IpcEndpointOptions options, + IServiceProvider serviceProvider, + IIpcMessageSerializer serializer, + IValueConverter valueConverter, + ILogger logger) { Name = name; - ServiceProvider = serviceProvider; - IncludeFailureDetailsInResponse = includeFailureDetailsInResponse; + _options = options; + _serviceProvider = serviceProvider; + _serializer = serializer; + _valueConverter = valueConverter; + _logger = logger; } public string Name { get; } - public IServiceProvider ServiceProvider { get; } - public bool IncludeFailureDetailsInResponse { get; } - - public abstract Task ListenAsync(CancellationToken cancellationToken = default(CancellationToken)); - } - - public abstract class IpcServiceEndpoint : IpcServiceEndpoint - where TContract : class - { - private readonly IValueConverter _converter; - private readonly IIpcMessageSerializer _serializer; - protected IpcServiceEndpoint(string name, IServiceProvider serviceProvider, bool includeFailureDetailsInResponse = false) - : base(name, serviceProvider, includeFailureDetailsInResponse) - { - _converter = serviceProvider.GetRequiredService(); - _serializer = serviceProvider.GetRequiredService(); - } + protected virtual Stream TransformStream(Stream input) => input; - protected async Task ProcessAsync(Stream server, ILogger logger, CancellationToken cancellationToken) + protected async Task ProcessAsync(Stream server, CancellationToken cancellationToken) { + server = TransformStream(server); using (var writer = new IpcWriter(server, _serializer, leaveOpen: true)) using (var reader = new IpcReader(server, _serializer, leaveOpen: true)) { @@ -52,36 +55,46 @@ protected async Task ProcessAsync(Stream server, ILogger logger, CancellationTok return; } - logger?.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] client connected, reading request..."); + _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] client connected, reading request..."); IpcRequest request = await reader.ReadIpcRequestAsync(cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); - logger?.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] request received, invoking '{request.MethodName}'..."); + _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] request received, invoking '{request.MethodName}'..."); IpcResponse response; - using (IServiceScope scope = ServiceProvider.CreateScope()) + using (IServiceScope scope = _serviceProvider.CreateScope()) { - response = await GetReponse(request, scope, logger).ConfigureAwait(false); + response = await GetReponse(request, scope).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); - logger?.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] sending response..."); + _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] sending response..."); await writer.WriteAsync(response, cancellationToken).ConfigureAwait(false); - logger?.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] done."); + _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] done."); } catch (Exception ex) when (!(ex is IpcServerException)) { - var response = IpcResponse.Fail(ex, IncludeFailureDetailsInResponse); - logger?.LogError(ex, response.Failure); + var response = IpcResponse.Fail(ex, _options.IncludeFailureDetailsInResponse); + _logger.LogError(ex, response.Failure); await writer.WriteAsync(response, cancellationToken).ConfigureAwait(false); } } } - protected async Task GetReponse(IpcRequest request, IServiceScope scope, ILogger logger) + protected async Task GetReponse(IpcRequest request, IServiceScope scope) { + if (request is null) + { + throw new ArgumentNullException(nameof(request)); + } + + if (scope is null) + { + throw new ArgumentNullException(nameof(scope)); + } + object service = scope.ServiceProvider.GetService(); if (service == null) { @@ -117,7 +130,7 @@ protected async Task GetReponse(IpcRequest request, IServiceScope s destType = request.GenericArguments[destType.GenericParameterPosition]; } - if (_converter.TryConvert(origValue, destType, out object arg)) + if (_valueConverter.TryConvert(origValue, destType, out object arg)) { args[i] = arg; } @@ -141,14 +154,14 @@ protected async Task GetReponse(IpcRequest request, IServiceScope s } catch (Exception ex) { - return IpcResponse.Fail(ex, IncludeFailureDetailsInResponse, true); + return IpcResponse.Fail(ex, _options.IncludeFailureDetailsInResponse, true); } if (@return is Task) { await ((Task)@return).ConfigureAwait(false); - var resultProperty = @return.GetType().GetProperty("Result"); + PropertyInfo resultProperty = @return.GetType().GetProperty("Result"); return IpcResponse.Success(resultProperty?.GetValue(@return)); } else @@ -158,8 +171,8 @@ protected async Task GetReponse(IpcRequest request, IServiceScope s } catch (Exception ex) when (!(ex is IpcServerException)) { - var response = IpcResponse.Fail(ex, IncludeFailureDetailsInResponse); - logger?.LogError(ex, response.Failure); + var response = IpcResponse.Fail(ex, _options.IncludeFailureDetailsInResponse); + _logger.LogError(ex, response.Failure); return response; } } @@ -179,16 +192,16 @@ public static MethodInfo GetUnambiguousMethod(IpcRequest request, object service MethodInfo method = null; // disambiguate - can't just call as before with generics - MethodInfo method = service.GetType().GetMethod(request.MethodName); - var types = service.GetType().GetInterfaces(); + Type[] types = service.GetType().GetInterfaces(); - var allMethods = types.SelectMany(t => t.GetMethods()); + System.Collections.Generic.IEnumerable allMethods = types.SelectMany(t => t.GetMethods()); var serviceMethods = allMethods.Where(t => t.Name == request.MethodName).ToList(); - foreach (var serviceMethod in serviceMethods) + foreach (MethodInfo serviceMethod in serviceMethods) { - var serviceMethodParameters = serviceMethod.GetParameters(); - var parameterTypeMatches = 0; + ParameterInfo[] serviceMethodParameters = serviceMethod.GetParameters(); + int parameterTypeMatches = 0; if (serviceMethodParameters.Length == request.Parameters.Length && serviceMethod.GetGenericArguments().Length == request.GenericArguments.Length) { @@ -218,5 +231,43 @@ public static MethodInfo GetUnambiguousMethod(IpcRequest request, object service return method; } + + public virtual Task StartAsync(CancellationToken cancellationToken = default) + { + Task.Run(() => + { + var semaphore = new SemaphoreSlim(_options.MaxConcurrentCalls); + while (!_cts.IsCancellationRequested) + { + semaphore.Wait(); + WaitAndProcessAsync(_cts.Token) + .ContinueWith(task => + { + if (task.IsFaulted) + { + _logger.LogError(task.Exception, "Error occurred"); + } + return semaphore.Release(); + }); + } + }); + + _logger.LogInformation("IPC endpoint '{EndpointName}' started.", Name); + return Task.CompletedTask; + } + + public virtual async Task StopAsync(CancellationToken cancellationToken = default) + { + _cts.Cancel(); + + await Task.Run(() => + { + WaitHandle.WaitAny(new[] { _cts.Token.WaitHandle }); + }, cancellationToken).ConfigureAwait(false); + + _logger.LogInformation("IPC endpoint '{EndpointName}' stopped.", Name); + } + + protected abstract Task WaitAndProcessAsync(CancellationToken cancellationToken); } } diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs b/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs new file mode 100644 index 0000000..2228366 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; + +namespace JKang.IpcServiceFramework.Hosting +{ + internal class IpcHostBuilder : IIpcHostBuilder + { + private readonly IHostBuilder _hostBuilder; + + public IpcHostBuilder(IHostBuilder hostBuilder) + { + _hostBuilder = hostBuilder ?? throw new ArgumentNullException(nameof(hostBuilder)); + + _hostBuilder.ConfigureServices((_, services) => + { + services + .TryAddIpcInternalServices() + .AddHostedService(); + }); + } + + public IIpcHostBuilder AddIpcEndpoint(Func endpointFactory) + { + if (endpointFactory is null) + { + throw new ArgumentNullException(nameof(endpointFactory)); + } + + _hostBuilder.ConfigureServices((_, services) => + { + services.AddSingleton(endpointFactory); + }); + + return this; + } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs b/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs new file mode 100644 index 0000000..88e726a --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace JKang.IpcServiceFramework.Hosting +{ + public class IpcHostedService : IHostedService + { + private readonly IEnumerable _endpoints; + private readonly ILogger _logger; + + public IpcHostedService( + IEnumerable endpoints, + ILogger logger) + { + _endpoints = endpoints; + _logger = logger; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + foreach (IIpcEndpoint endpoint in _endpoints) + { + await endpoint.StartAsync(cancellationToken).ConfigureAwait(false); + } + _logger.LogInformation("IPC hosted service started."); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + foreach (IIpcEndpoint endpoint in _endpoints) + { + await endpoint.StopAsync(cancellationToken).ConfigureAwait(false); + } + _logger.LogInformation("IPC hosted service stopped."); + } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj b/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj new file mode 100644 index 0000000..580c045 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.IntegrationTests/ContractTest.cs b/src/JKang.IpcServiceFramework.IntegrationTests/ContractTest.cs deleted file mode 100644 index ce73e82..0000000 --- a/src/JKang.IpcServiceFramework.IntegrationTests/ContractTest.cs +++ /dev/null @@ -1,127 +0,0 @@ -using AutoFixture.Xunit2; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Net; -using System.Numerics; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace JKang.IpcServiceFramework.IntegrationTests -{ - public class ContractTest : IDisposable - { - private readonly CancellationTokenSource _cancellationToken; - private readonly int _port; - private readonly IpcServiceClient _client; - - public ContractTest() - { - // configure DI - IServiceCollection services = new ServiceCollection() - .AddIpc(builder => builder.AddNamedPipe().AddService()); - _port = new Random().Next(10000, 50000); - IIpcServiceHost host = new IpcServiceHostBuilder(services.BuildServiceProvider()) - .AddTcpEndpoint( - name: Guid.NewGuid().ToString(), - ipEndpoint: IPAddress.Loopback, - port: _port) - .Build(); - _cancellationToken = new CancellationTokenSource(); - host.RunAsync(_cancellationToken.Token); - - _client = new IpcServiceClientBuilder() - .UseTcp(IPAddress.Loopback, _port) - .Build(); - } - - [Theory, AutoData] - public async Task SimpleType(float a, float b) - { - float actual = await _client.InvokeAsync(x => x.AddFloat(a, b)); - float expected = new TestService().AddFloat(a, b); - Assert.Equal(expected, actual); - } - - [Theory, AutoData] - public async Task ComplexType(Complex a, Complex b) - { - Complex actual = await _client.InvokeAsync(x => x.AddComplex(a, b)); - Complex expected = new TestService().AddComplex(a, b); - Assert.Equal(expected, actual); - } - - [Theory, AutoData] - public async Task ComplexTypeArray(IEnumerable array) - { - Complex actual = await _client.InvokeAsync(x => x.SumComplexArray(array)); - Complex expected = new TestService().SumComplexArray(array); - Assert.Equal(expected, actual); - } - - [Fact] - public async Task ReturnVoid() - { - await _client.InvokeAsync(x => x.DoNothing()); - } - - [Theory, InlineData(" 2008-06-11T16:11:20.0904778Z", DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowWhiteSpaces)] - public async Task EnumParameter(string value, DateTimeStyles styles) - { - DateTime actual = await _client.InvokeAsync(x => x.ParseDate(value, styles)); - DateTime expected = new TestService().ParseDate(value, styles); - Assert.Equal(expected, actual); - } - - [Theory, AutoData] - public async Task ByteArray(byte[] input) - { - byte[] actual = await _client.InvokeAsync(x => x.ReverseBytes(input)); - byte[] expected = new TestService().ReverseBytes(input); - Assert.Equal(expected, actual); - } - - [Fact] - public async Task GenericParameter() - { - decimal actual = await _client.InvokeAsync(x => x.GetDefaultValue()); - decimal expected = new TestService().GetDefaultValue(); - Assert.Equal(expected, actual); - } - - [Fact] - public async Task AsyncOperation() - { - long actual = await _client.InvokeAsync(x => x.WaitAsync(500)); - Assert.True(actual >= 450); - } - - [Fact] - public async Task ExplicitInterfaceOperation() - { - int actual = await _client.InvokeAsync(x => x.ExplicitInterfaceMember()); - Assert.True(actual == 0); - } - - [Fact] - public async Task ThrowException() - { - try - { - await _client.InvokeAsync(x => x.ThrowException("This was forced")); - } - catch (IpcServerException ex) - { - Assert.Contains("This was forced", ex.Message); - Assert.DoesNotContain(IpcServerException.ServerFailureDetails, ex.ToString()); - } - } - - public void Dispose() - { - _cancellationToken.Cancel(); - } - } -} diff --git a/src/JKang.IpcServiceFramework.IntegrationTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.IntegrationTests/EdgeCaseTest.cs deleted file mode 100644 index fe98fc9..0000000 --- a/src/JKang.IpcServiceFramework.IntegrationTests/EdgeCaseTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using AutoFixture.Xunit2; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net; -using System.Numerics; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace JKang.IpcServiceFramework.IntegrationTests -{ - public class EdgeCaseTest : IDisposable - { - private readonly CancellationTokenSource _cancellationToken; - private readonly int _port; - private readonly IpcServiceClient _client; - - public EdgeCaseTest() - { - // configure DI - IServiceCollection services = new ServiceCollection() - .AddIpc(builder => builder.AddNamedPipe().AddService()); - _port = new Random().Next(10000, 50000); - IIpcServiceHost host = new IpcServiceHostBuilder(services.BuildServiceProvider()) - .AddTcpEndpoint( - name: Guid.NewGuid().ToString(), - ipEndpoint: IPAddress.Loopback, - port: _port) - .Build(); - _cancellationToken = new CancellationTokenSource(); - host.RunAsync(_cancellationToken.Token); - - _client = new IpcServiceClientBuilder() - .UseTcp(IPAddress.Loopback, _port) - .Build(); - } - - [Fact] - public async Task HugeMessage() - { - byte[] buffer = new byte[100000000]; // 100MB - new Random().NextBytes(buffer); - byte[] result = await _client.InvokeAsync(x => x.ReverseBytes(buffer)); - } - - public void Dispose() - { - _cancellationToken.Cancel(); - } - } -} diff --git a/src/JKang.IpcServiceFramework.IntegrationTests/FailureDetailTest.cs b/src/JKang.IpcServiceFramework.IntegrationTests/FailureDetailTest.cs deleted file mode 100644 index 5f5d5d9..0000000 --- a/src/JKang.IpcServiceFramework.IntegrationTests/FailureDetailTest.cs +++ /dev/null @@ -1,60 +0,0 @@ -using AutoFixture.Xunit2; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Net; -using System.Numerics; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace JKang.IpcServiceFramework.IntegrationTests -{ - public class FailureDetailTest : IDisposable - { - private readonly CancellationTokenSource _cancellationToken; - private readonly int _port; - private readonly IpcServiceClient _client; - - public FailureDetailTest() - { - // configure DI - IServiceCollection services = new ServiceCollection() - .AddIpc(builder => builder.AddNamedPipe().AddService()); - _port = new Random().Next(10000, 50000); - IIpcServiceHost host = new IpcServiceHostBuilder(services.BuildServiceProvider()) - .AddTcpEndpoint( - name: Guid.NewGuid().ToString(), - ipEndpoint: IPAddress.Loopback, - port: _port, - includeFailureDetailsInResponse: true) - .Build(); - _cancellationToken = new CancellationTokenSource(); - host.RunAsync(_cancellationToken.Token); - - _client = new IpcServiceClientBuilder() - .UseTcp(IPAddress.Loopback, _port) - .Build(); - } - - [Fact] - public async Task ThrowException() - { - try - { - await _client.InvokeAsync(x => x.ThrowException("This was forced")); - } - catch (IpcServerException ex) - { - Assert.Contains("This was forced", ex.Message); - Assert.Contains(IpcServerException.ServerFailureDetails, ex.ToString()); - } - } - - public void Dispose() - { - _cancellationToken.Cancel(); - } - } -} diff --git a/src/JKang.IpcServiceFramework.IntegrationTests/ITestService.cs b/src/JKang.IpcServiceFramework.IntegrationTests/ITestService.cs deleted file mode 100644 index a8848d0..0000000 --- a/src/JKang.IpcServiceFramework.IntegrationTests/ITestService.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Numerics; -using System.Threading.Tasks; - -namespace JKang.IpcServiceFramework.IntegrationTests -{ - public interface ITestService - { - float AddFloat(float x, float y); - Complex AddComplex(Complex x, Complex y); - Complex SumComplexArray(IEnumerable input); - void DoNothing(); - DateTime ParseDate(string value, DateTimeStyles styles); - byte[] ReverseBytes(byte[] input); - T GetDefaultValue(); - Task WaitAsync(int milliseconds); - int ExplicitInterfaceMember(); - void ThrowException(string message); - } -} diff --git a/src/JKang.IpcServiceFramework.IntegrationTests/JKang.IpcServiceFramework.IntegrationTests.csproj b/src/JKang.IpcServiceFramework.IntegrationTests/JKang.IpcServiceFramework.IntegrationTests.csproj deleted file mode 100644 index ba52e8a..0000000 --- a/src/JKang.IpcServiceFramework.IntegrationTests/JKang.IpcServiceFramework.IntegrationTests.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - netcoreapp2.1 - - false - - - - - - - - - - - - - - - - diff --git a/src/JKang.IpcServiceFramework.IntegrationTests/TestService.cs b/src/JKang.IpcServiceFramework.IntegrationTests/TestService.cs deleted file mode 100644 index 2ffffe4..0000000 --- a/src/JKang.IpcServiceFramework.IntegrationTests/TestService.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using System.Threading.Tasks; - -namespace JKang.IpcServiceFramework.IntegrationTests -{ - public class TestService : ITestService - { - public Complex AddComplex(Complex x, Complex y) - { - return x + y; - } - - public float AddFloat(float x, float y) - { - return x + y; - } - - public void DoNothing() { } - - public DateTime ParseDate(string value, DateTimeStyles styles) - { - return DateTime.Parse(value, CultureInfo.InvariantCulture, styles); - } - - public Complex SumComplexArray(IEnumerable input) - { - var result = new Complex(); - foreach (Complex value in input) - { - result += value; - } - return result; - } - - public byte[] ReverseBytes(byte[] input) - { - return input.Reverse().ToArray(); - } - - public T GetDefaultValue() - { - return default(T); - } - - public async Task WaitAsync(int milliseconds) - { - var sw = Stopwatch.StartNew(); - await Task.Delay(milliseconds); - return sw.ElapsedMilliseconds; - } - - int ITestService.ExplicitInterfaceMember() - { - return 0; - } - - public void ThrowException(string message) - { - throw new Exception(message); - } - } -} diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs new file mode 100644 index 0000000..1b62039 --- /dev/null +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs @@ -0,0 +1,84 @@ +using AutoFixture.Xunit2; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.Testing; +using JKang.IpcServiceFramework.Testing.Fixtures; +using JKang.IpcServiceFramework.Testing.TestCases; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Numerics; +using System.Threading.Tasks; +using Xunit; + +namespace JKang.IpcServiceFramework.NamedPipeTests +{ + public class ContractTest : IClassFixture> + { + private readonly ContractTestCase _testCase; + + public ContractTest(IpcApplicationFactory factory) + { + string pipeName = Guid.NewGuid().ToString(); + var serviceMock = new Mock(); + IIpcClient client = factory + .WithServiceImplementation(_ => serviceMock.Object) + .WithIpcHostConfiguration(hostBuilder => + { + hostBuilder.AddNamedPipeEndpoint("default", pipeName); + }) + .CreateClient(services => + { + services.AddNamedPipeIpcClient(pipeName); + }); + _testCase = new ContractTestCase(serviceMock, client); + } + + [Theory, AutoData] + public Task PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g, int h, uint i, + long j, ulong k, short l, ushort m, int expected) + => _testCase.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m, expected); + + [Theory, AutoData] + public Task StringType(string input, string expected) + => _testCase.StringType(input, expected); + + [Theory, AutoData] + public Task ComplexType(Complex input, Complex expected) + => _testCase.ComplexType(input, expected); + + [Theory, AutoData] + public Task ComplexTypeArray(IEnumerable input, IEnumerable expected) + => _testCase.ComplexTypeArray(input, expected); + + [Fact] + public Task ReturnVoid() + => _testCase.ReturnVoid(); + + [Theory, AutoData] + public Task DateTime(DateTime input, DateTime expected) + => _testCase.DateTime(input, expected); + + [Theory, AutoData] + public Task EnumType(DateTimeStyles input, DateTimeStyles expected) + => _testCase.EnumType(input, expected); + + [Theory, AutoData] + public Task ByteArray(byte[] input, byte[] expected) + => _testCase.ByteArray(input, expected); + + [Theory, AutoData] + public Task GenericMethod(decimal input, decimal expected) + => _testCase.GenericMethod(input, expected); + + [Theory, AutoData] + public Task AsyncMethod(int expected) + => _testCase.AsyncMethod(expected); + + [Theory, AutoData] + public Task ThrowException(Exception expected) + => _testCase.ThrowException(expected); + } +} diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/JKang.IpcServiceFramework.NamedPipeTests.csproj b/src/JKang.IpcServiceFramework.NamedPipeTests/JKang.IpcServiceFramework.NamedPipeTests.csproj new file mode 100644 index 0000000..210aecb --- /dev/null +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/JKang.IpcServiceFramework.NamedPipeTests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Server/IIpcServiceBuilder.cs b/src/JKang.IpcServiceFramework.Server/IIpcServiceBuilder.cs deleted file mode 100644 index c386869..0000000 --- a/src/JKang.IpcServiceFramework.Server/IIpcServiceBuilder.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace JKang.IpcServiceFramework -{ - public interface IIpcServiceBuilder - { - IServiceCollection Services { get; } - - IIpcServiceBuilder AddService() - where TInterface: class - where TImplementation: class, TInterface; - - IIpcServiceBuilder AddService(Func implementationFactory) - where TInterface : class - where TImplementation : class, TInterface; - - IIpcServiceBuilder AddService(Func implementationFactory) - where TInterface : class; - } -} \ No newline at end of file diff --git a/src/JKang.IpcServiceFramework.Server/IIpcServiceHost.cs b/src/JKang.IpcServiceFramework.Server/IIpcServiceHost.cs deleted file mode 100644 index be03611..0000000 --- a/src/JKang.IpcServiceFramework.Server/IIpcServiceHost.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace JKang.IpcServiceFramework -{ - public interface IIpcServiceHost - { - void Run(); - - Task RunAsync(CancellationToken cancellationToken = default(CancellationToken)); - } -} \ No newline at end of file diff --git a/src/JKang.IpcServiceFramework.Server/IpcServiceBuilder.cs b/src/JKang.IpcServiceFramework.Server/IpcServiceBuilder.cs deleted file mode 100644 index 6d0b615..0000000 --- a/src/JKang.IpcServiceFramework.Server/IpcServiceBuilder.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace JKang.IpcServiceFramework -{ - internal class IpcServiceBuilder : IIpcServiceBuilder - { - public IpcServiceBuilder(IServiceCollection services) - { - Services = services; - } - - public IServiceCollection Services { get; } - - public IIpcServiceBuilder AddService() - where TInterface : class - where TImplementation : class, TInterface - { - Services.AddScoped(); - return this; - } - - public IIpcServiceBuilder AddService(Func implementationFactory) - where TInterface : class - where TImplementation : class, TInterface - { - Services.AddScoped(implementationFactory); - return this; - } - - public IIpcServiceBuilder AddService(Func implementationFactory) - where TInterface : class - { - Services.AddScoped(implementationFactory); - return this; - } - } -} \ No newline at end of file diff --git a/src/JKang.IpcServiceFramework.Server/IpcServiceHost.cs b/src/JKang.IpcServiceFramework.Server/IpcServiceHost.cs deleted file mode 100644 index 5a5bbd0..0000000 --- a/src/JKang.IpcServiceFramework.Server/IpcServiceHost.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace JKang.IpcServiceFramework -{ - public class IpcServiceHost : IIpcServiceHost - { - private readonly List _endpoints; - private readonly IServiceProvider _serviceProvider; - private readonly ILogger _logger; - - public IpcServiceHost(IEnumerable endpoints, IServiceProvider serviceProvider) - { - _endpoints = endpoints.ToList(); - _serviceProvider = serviceProvider; - _logger = _serviceProvider.GetService>(); - } - - public void Run() - { - RunAsync().Wait(); - } - - public Task RunAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - Task[] tasks = _endpoints - .Select(endpoint => - { - _logger.LogDebug($"Starting endpoint '{endpoint.Name}'..."); - - cancellationToken.Register(() => - { - _logger.LogDebug($"Stopping endpoint '{endpoint.Name}'..."); - }); - - return endpoint.ListenAsync(cancellationToken).ContinueWith(_ => - { - _logger.LogDebug($"Endpoint '{endpoint.Name}' stopped."); - }); - }) - .ToArray(); - return Task.WhenAll(tasks); - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/IpcServiceHostBuilder.cs b/src/JKang.IpcServiceFramework.Server/IpcServiceHostBuilder.cs deleted file mode 100644 index c0ca17c..0000000 --- a/src/JKang.IpcServiceFramework.Server/IpcServiceHostBuilder.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace JKang.IpcServiceFramework -{ - public class IpcServiceHostBuilder - { - private readonly List _endpoints = new List(); - - public IpcServiceHostBuilder(IServiceProvider serviceProvider) - { - ServiceProvider = serviceProvider; - } - - public IServiceProvider ServiceProvider { get; } - - public IpcServiceHostBuilder AddEndpoint(IpcServiceEndpoint endpoint) - { - _endpoints.Add(endpoint); - return this; - } - - public IIpcServiceHost Build() - { - return new IpcServiceHost(_endpoints, ServiceProvider); - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/IpcServiceOptions.cs b/src/JKang.IpcServiceFramework.Server/IpcServiceOptions.cs deleted file mode 100644 index bbdeb7c..0000000 --- a/src/JKang.IpcServiceFramework.Server/IpcServiceOptions.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace JKang.IpcServiceFramework -{ - [Obsolete] - public class IpcServiceOptions - { - public int ThreadCount { get; set; } = 4; - } -} diff --git a/src/JKang.IpcServiceFramework.Server/IpcServiceServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Server/IpcServiceServiceCollectionExtensions.cs deleted file mode 100644 index e3b31dd..0000000 --- a/src/JKang.IpcServiceFramework.Server/IpcServiceServiceCollectionExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using JKang.IpcServiceFramework.Services; -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace JKang.IpcServiceFramework -{ - public static class IpcServiceServiceCollectionExtensions - { - [Obsolete("Use services.AddIpc(builder => { ... }) instead.")] - public static IIpcServiceBuilder AddIpc(this IServiceCollection services) - { - IIpcServiceBuilder builder = null; - services.AddIpc(x => builder = x); - return builder; - } - - public static IServiceCollection AddIpc(this IServiceCollection services, Action configAction) - { - services - .AddLogging() - .AddScoped() - .AddScoped(); - - var builder = new IpcServiceBuilder(services); - configAction?.Invoke(builder); - return services; - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/JKang.IpcServiceFramework.Server.csproj b/src/JKang.IpcServiceFramework.Server/JKang.IpcServiceFramework.Server.csproj deleted file mode 100644 index 3ff37f9..0000000 --- a/src/JKang.IpcServiceFramework.Server/JKang.IpcServiceFramework.Server.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netstandard2.0 - JKang.IpcServiceFramework - true - - - - - - - - - - - - diff --git a/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceCollectionExtensions.cs deleted file mode 100644 index 86333b9..0000000 --- a/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceCollectionExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace JKang.IpcServiceFramework -{ - public static class NamedPipeIpcServiceCollectionExtensions - { - public static IIpcServiceBuilder AddNamedPipe(this IIpcServiceBuilder builder) - { - return builder.AddNamedPipe(null); - } - - public static IIpcServiceBuilder AddNamedPipe(this IIpcServiceBuilder builder, Action configure) - { - var options = new NamedPipeOptions(); - configure?.Invoke(options); - - builder.Services - .AddSingleton(options) - ; - - return builder; - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceEndpoint.cs b/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceEndpoint.cs deleted file mode 100644 index e2c91d8..0000000 --- a/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceEndpoint.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.IO.Pipes; -using System.Threading; -using System.Threading.Tasks; - -namespace JKang.IpcServiceFramework.NamedPipe -{ - public class NamedPipeIpcServiceEndpoint : IpcServiceEndpoint - where TContract : class - { - private readonly ILogger> _logger; - private readonly NamedPipeOptions _options; - - public NamedPipeIpcServiceEndpoint(string name, IServiceProvider serviceProvider, string pipeName, bool includeFailureDetailsInResponse = false) - : base(name, serviceProvider, includeFailureDetailsInResponse) - { - PipeName = pipeName; - - _logger = serviceProvider.GetService>>(); - _options = serviceProvider.GetRequiredService(); - } - - public string PipeName { get; } - - public override Task ListenAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - NamedPipeOptions options = ServiceProvider.GetRequiredService(); - - var threads = new Thread[options.ThreadCount]; - for (int i = 0; i < threads.Length; i++) - { - threads[i] = new Thread((a) => { StartServerThread(a).ConfigureAwait(false).GetAwaiter().GetResult(); }); - threads[i].Start(cancellationToken); - } - - return Task.Factory.StartNew(() => - { - _logger.LogDebug($"Endpoint '{Name}' listening on pipe '{PipeName}'..."); - while (!cancellationToken.IsCancellationRequested) - { - Thread.Sleep(100); - - for (int i = 0; i < threads.Length; i++) - { - if (threads[i].Join(250)) - { - // thread is finished, starting a new thread - threads[i] = new Thread((a) => { StartServerThread(a).ConfigureAwait(false).GetAwaiter().GetResult(); }); - threads[i].Start(cancellationToken); - } - } - } - }); - } - - private async Task StartServerThread(object obj) - { - var token = (CancellationToken)obj; - try - { - using (var server = new NamedPipeServerStream(PipeName, PipeDirection.InOut, _options.ThreadCount, - PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) - { - await server.WaitForConnectionAsync(token).ConfigureAwait(false); - await ProcessAsync(server, _logger, token).ConfigureAwait(false); - } - } - catch when (token.IsCancellationRequested) - { - } - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceHostBuilderExtensions.cs deleted file mode 100644 index 309c2df..0000000 --- a/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeIpcServiceHostBuilderExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using JKang.IpcServiceFramework.NamedPipe; - -namespace JKang.IpcServiceFramework -{ - public static class NamedPipeIpcServiceHostBuilderExtensions - { - public static IpcServiceHostBuilder AddNamedPipeEndpoint(this IpcServiceHostBuilder builder, - string name, string pipeName, bool includeFailureDetailsInResponse = false) - where TContract: class - { - return builder.AddEndpoint(new NamedPipeIpcServiceEndpoint(name, builder.ServiceProvider, pipeName, - includeFailureDetailsInResponse: includeFailureDetailsInResponse)); - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeOptions.cs b/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeOptions.cs deleted file mode 100644 index eab7d2c..0000000 --- a/src/JKang.IpcServiceFramework.Server/NamedPipe/NamedPipeOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JKang.IpcServiceFramework -{ - public class NamedPipeOptions - { - public int ThreadCount { get; set; } = 4; - } -} diff --git a/src/JKang.IpcServiceFramework.Server/Tcp/TcpConcurrencyOptions.cs b/src/JKang.IpcServiceFramework.Server/Tcp/TcpConcurrencyOptions.cs deleted file mode 100644 index c57f79d..0000000 --- a/src/JKang.IpcServiceFramework.Server/Tcp/TcpConcurrencyOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace JKang.IpcServiceFramework.Tcp -{ - public class TcpConcurrencyOptions - { - public int MaximumConcurrentCalls; - - public TcpConcurrencyOptions(int maximumConcurrentCalls) - { - this.MaximumConcurrentCalls = maximumConcurrentCalls; - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceCollectionExtensions.cs deleted file mode 100644 index 0953b4e..0000000 --- a/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceCollectionExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JKang.IpcServiceFramework -{ - public static class TcpIpcServiceCollectionExtensions - { - public static IIpcServiceBuilder AddTcp(this IIpcServiceBuilder builder) - { - return builder; - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceEndpoint.cs b/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceEndpoint.cs deleted file mode 100644 index ffe382b..0000000 --- a/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceEndpoint.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.DependencyInjection; -using System.Threading; -using System.Threading.Tasks; -using System.Security.Cryptography.X509Certificates; -using System.IO; -using System.Net.Security; - -namespace JKang.IpcServiceFramework.Tcp -{ - public class TcpIpcServiceEndpoint : IpcServiceEndpoint - where TContract : class - { - private readonly ILogger> _logger; - - public int Port { get; private set; } - - public bool SSL { get; private set; } - - private readonly TcpListener _listener; - private readonly Func _streamTranslator; - private readonly X509Certificate _serverCertificate; - private readonly int _maximumConcurrentCalls; - - public TcpIpcServiceEndpoint(String name, IServiceProvider serviceProvider, IPAddress ipEndpoint, int port, TcpConcurrencyOptions concurrencyOptions, bool includeFailureDetailsInResponse = false) - : base(name, serviceProvider, includeFailureDetailsInResponse) - { - if (concurrencyOptions == null) - _maximumConcurrentCalls = 1; - else - { - _maximumConcurrentCalls = concurrencyOptions.MaximumConcurrentCalls; - - if (_maximumConcurrentCalls < 1) - _maximumConcurrentCalls = 1; - } - - _listener = new TcpListener(ipEndpoint, port); - _logger = serviceProvider.GetService>>(); - Port = port; - } - - public TcpIpcServiceEndpoint(String name, IServiceProvider serviceProvider, IPAddress ipEndpoint, TcpConcurrencyOptions concurrencyOptions, bool includeFailureDetailsInResponse = false) - : this(name, serviceProvider, ipEndpoint, 0, concurrencyOptions, includeFailureDetailsInResponse) - { - } - - public TcpIpcServiceEndpoint(String name, IServiceProvider serviceProvider, IPAddress ipEndpoint, Func streamTranslator, TcpConcurrencyOptions concurrencyOptions, bool includeFailureDetailsInResponse = false) - : this(name, serviceProvider, ipEndpoint, 0, concurrencyOptions, includeFailureDetailsInResponse) - { - _streamTranslator = streamTranslator; - } - - public TcpIpcServiceEndpoint(String name, IServiceProvider serviceProvider, IPAddress ipEndpoint, X509Certificate sslCertificate, TcpConcurrencyOptions concurrencyOptions, bool includeFailureDetailsInResponse = false) - : this(name, serviceProvider, ipEndpoint, 0, concurrencyOptions, includeFailureDetailsInResponse) - { - _serverCertificate = sslCertificate; - SSL = true; - } - - public TcpIpcServiceEndpoint(String name, IServiceProvider serviceProvider, IPAddress ipEndpoint, X509Certificate sslCertificate, Func streamTranslator, TcpConcurrencyOptions concurrencyOptions, bool includeFailureDetailsInResponse = false) - : this(name, serviceProvider, ipEndpoint, sslCertificate, concurrencyOptions, includeFailureDetailsInResponse) - { - _streamTranslator = streamTranslator; - } - - public TcpIpcServiceEndpoint(String name, IServiceProvider serviceProvider, IPAddress ipEndpoint, int port, Func streamTranslator, TcpConcurrencyOptions concurrencyOptions, bool includeFailureDetailsInResponse = false) - : this(name, serviceProvider, ipEndpoint, port, concurrencyOptions, includeFailureDetailsInResponse) - { - _streamTranslator = streamTranslator; - } - - public TcpIpcServiceEndpoint(String name, IServiceProvider serviceProvider, IPAddress ipEndpoint, int port, X509Certificate sslCertificate, TcpConcurrencyOptions concurrencyOptions, bool includeFailureDetailsInResponse = false) - : this(name, serviceProvider, ipEndpoint, port, concurrencyOptions, includeFailureDetailsInResponse) - { - _serverCertificate = sslCertificate; - SSL = true; - } - - public TcpIpcServiceEndpoint(String name, IServiceProvider serviceProvider, IPAddress ipEndpoint, int port, X509Certificate sslCertificate, Func streamTranslator, TcpConcurrencyOptions concurrencyOptions, bool includeFailureDetailsInResponse = false) - : this(name, serviceProvider, ipEndpoint, port, sslCertificate, concurrencyOptions, includeFailureDetailsInResponse) - { - _streamTranslator = streamTranslator; - } - - public override Task ListenAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - _listener.Start(); - - // If port is dynamically assigned, get the port number after start - Port = ((IPEndPoint)_listener.LocalEndpoint).Port; - - cancellationToken.Register(() => - { - _listener.Stop(); - }); - - return Task.Run(async () => - { - try - { - SemaphoreSlim _throttle = null; - - if (_maximumConcurrentCalls > 1) - _throttle = new SemaphoreSlim(_maximumConcurrentCalls); - - _logger.LogDebug($"Endpoint '{Name}' listening on port {Port}..."); - - while (true) - { - if (_throttle != null) - await _throttle.WaitAsync(); - - TcpClient client = await _listener.AcceptTcpClientAsync().ConfigureAwait(false); - - Stream server = client.GetStream(); - - // if there's a stream translator, apply it here - if (_streamTranslator != null) - { - server = _streamTranslator(server); - } - - // if SSL is enabled, wrap the stream in an SslStream in client mode - if (SSL) - { - var ssl = new SslStream(server, false); - ssl.AuthenticateAsServer(_serverCertificate); - server = ssl; - } - - if (_throttle == null) - { - await ProcessAsync(server, _logger, cancellationToken); - client.Close(); - } - else - { - var processTask = Task.Run( - async () => - { - try - { - await ProcessAsync(server, _logger, cancellationToken).ConfigureAwait(false); - client.Close(); - } - catch when (cancellationToken.IsCancellationRequested) { } - finally - { - _throttle.Release(); - } - }); - } - } - } - catch when (cancellationToken.IsCancellationRequested) - { } - }); - } - } -} diff --git a/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceHostBuilderExtensions.cs deleted file mode 100644 index 739ae7a..0000000 --- a/src/JKang.IpcServiceFramework.Server/Tcp/TcpIpcServiceHostBuilderExtensions.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using JKang.IpcServiceFramework.Tcp; - -namespace JKang.IpcServiceFramework -{ - public static class TcpIpcServiceHostBuilderExtensions - { - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddTcpEndpoint(name, ipEndpoint, concurrencyOptions: null, - includeFailureDetailsInResponse: includeFailureDetailsInResponse); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, TcpConcurrencyOptions concurrencyOptions, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddEndpoint(new TcpIpcServiceEndpoint(name, builder.ServiceProvider, ipEndpoint, concurrencyOptions)); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, int port, bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddTcpEndpoint(name, ipEndpoint, port, concurrencyOptions: null, - includeFailureDetailsInResponse: includeFailureDetailsInResponse); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, int port, TcpConcurrencyOptions concurrencyOptions, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddEndpoint(new TcpIpcServiceEndpoint(name, builder.ServiceProvider, ipEndpoint, port, concurrencyOptions, - includeFailureDetailsInResponse: includeFailureDetailsInResponse)); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, Func streamTranslator, bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddTcpEndpoint(name, ipEndpoint, streamTranslator, concurrencyOptions: null, - includeFailureDetailsInResponse: includeFailureDetailsInResponse); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, Func streamTranslator, TcpConcurrencyOptions concurrencyOptions, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddEndpoint(new TcpIpcServiceEndpoint(name, builder.ServiceProvider, ipEndpoint, streamTranslator, concurrencyOptions, - includeFailureDetailsInResponse: includeFailureDetailsInResponse)); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, X509Certificate sslCertificate, bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddTcpEndpoint(name, ipEndpoint, sslCertificate, concurrencyOptions: null, - includeFailureDetailsInResponse: includeFailureDetailsInResponse); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, X509Certificate sslCertificate, TcpConcurrencyOptions concurrencyOptions, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddEndpoint(new TcpIpcServiceEndpoint(name, builder.ServiceProvider, ipEndpoint, sslCertificate, concurrencyOptions, - includeFailureDetailsInResponse: includeFailureDetailsInResponse)); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, X509Certificate sslCertificate, Func streamTranslator, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddTcpEndpoint(name, ipEndpoint, sslCertificate, streamTranslator, concurrencyOptions: null, - includeFailureDetailsInResponse: includeFailureDetailsInResponse); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, X509Certificate sslCertificate, Func streamTranslator, TcpConcurrencyOptions concurrencyOptions, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddEndpoint(new TcpIpcServiceEndpoint(name, builder.ServiceProvider, ipEndpoint, sslCertificate, streamTranslator, concurrencyOptions, - includeFailureDetailsInResponse: includeFailureDetailsInResponse)); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, int port, X509Certificate sslCertificate, bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddTcpEndpoint(name, ipEndpoint, port, sslCertificate, concurrencyOptions: null, - includeFailureDetailsInResponse: includeFailureDetailsInResponse); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, int port, X509Certificate sslCertificate, TcpConcurrencyOptions concurrencyOptions, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddEndpoint(new TcpIpcServiceEndpoint(name, builder.ServiceProvider, ipEndpoint, port, sslCertificate, concurrencyOptions, - includeFailureDetailsInResponse: includeFailureDetailsInResponse)); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, int port, Func streamTranslator, bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddTcpEndpoint(name, ipEndpoint, port, streamTranslator, concurrencyOptions: null, - includeFailureDetailsInResponse: includeFailureDetailsInResponse); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, int port, Func streamTranslator, TcpConcurrencyOptions concurrencyOptions, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddEndpoint(new TcpIpcServiceEndpoint(name, builder.ServiceProvider, ipEndpoint, port, streamTranslator, concurrencyOptions)); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, int port, X509Certificate sslCertificate, Func streamTranslator, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddTcpEndpoint(name, ipEndpoint, port, sslCertificate, streamTranslator, concurrencyOptions: null, - includeFailureDetailsInResponse: includeFailureDetailsInResponse); - } - - public static IpcServiceHostBuilder AddTcpEndpoint(this IpcServiceHostBuilder builder, - string name, IPAddress ipEndpoint, int port, X509Certificate sslCertificate, Func streamTranslator, TcpConcurrencyOptions concurrencyOptions, - bool includeFailureDetailsInResponse = false) - where TContract : class - { - return builder.AddEndpoint(new TcpIpcServiceEndpoint(name, builder.ServiceProvider, ipEndpoint, port, sslCertificate, streamTranslator, concurrencyOptions, - includeFailureDetailsInResponse: includeFailureDetailsInResponse)); - } - } -} diff --git a/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs b/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs new file mode 100644 index 0000000..4f61561 --- /dev/null +++ b/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs @@ -0,0 +1,86 @@ +using AutoFixture.Xunit2; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.Testing; +using JKang.IpcServiceFramework.Testing.Fixtures; +using JKang.IpcServiceFramework.Testing.TestCases; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net; +using System.Numerics; +using System.Threading.Tasks; +using Xunit; + +namespace JKang.IpcServiceFramework.TcpTests +{ + public class ContractTest : IClassFixture> + { + private static readonly Random _rand = new Random(); + private readonly ContractTestCase _testCase; + + public ContractTest(IpcApplicationFactory factory) + { + int port = _rand.Next(10000, 50000); + var serviceMock = new Mock(); + IIpcClient client = factory + .WithServiceImplementation(_ => serviceMock.Object) + .WithIpcHostConfiguration(hostBuilder => + { + hostBuilder.AddTcpEndpoint("default", IPAddress.Loopback, port); + }) + .CreateClient(services => + { + services.AddTcpIpcClient(IPAddress.Loopback, port); + }); + _testCase = new ContractTestCase(serviceMock, client); + } + + [Theory, AutoData] + public Task PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g, int h, uint i, + long j, ulong k, short l, ushort m, int expected) + => _testCase.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m, expected); + + [Theory, AutoData] + public Task StringType(string input, string expected) + => _testCase.StringType(input, expected); + + [Theory, AutoData] + public Task ComplexType(Complex input, Complex expected) + => _testCase.ComplexType(input, expected); + + [Theory, AutoData] + public Task ComplexTypeArray(IEnumerable input, IEnumerable expected) + => _testCase.ComplexTypeArray(input, expected); + + [Fact] + public Task ReturnVoid() + => _testCase.ReturnVoid(); + + [Theory, AutoData] + public Task DateTime(DateTime input, DateTime expected) + => _testCase.DateTime(input, expected); + + [Theory, AutoData] + public Task EnumType(DateTimeStyles input, DateTimeStyles expected) + => _testCase.EnumType(input, expected); + + [Theory, AutoData] + public Task ByteArray(byte[] input, byte[] expected) + => _testCase.ByteArray(input, expected); + + [Theory, AutoData] + public Task GenericMethod(decimal input, decimal expected) + => _testCase.GenericMethod(input, expected); + + [Theory, AutoData] + public Task AsyncMethod(int expected) + => _testCase.AsyncMethod(expected); + + [Theory, AutoData] + public Task ThrowException(Exception expected) + => _testCase.ThrowException(expected); + } +} diff --git a/src/JKang.IpcServiceFramework.TcpTests/JKang.IpcServiceFramework.TcpTests.csproj b/src/JKang.IpcServiceFramework.TcpTests/JKang.IpcServiceFramework.TcpTests.csproj new file mode 100644 index 0000000..c8959f0 --- /dev/null +++ b/src/JKang.IpcServiceFramework.TcpTests/JKang.IpcServiceFramework.TcpTests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs b/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs new file mode 100644 index 0000000..bc3ec6f --- /dev/null +++ b/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Numerics; +using System.Threading.Tasks; + +namespace JKang.IpcServiceFramework.Testing.Fixtures +{ + public interface ITestService + { + int PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g, int h, uint i, long j, + ulong k, short l, ushort m); + string StringType(string input); + Complex ComplexType(Complex input); + IEnumerable ComplexTypeArray(IEnumerable input); + void ReturnVoid(); + DateTime DateTime(DateTime input); + DateTimeStyles EnumType(DateTimeStyles input); + byte[] ByteArray(byte[] input); + T GenericMethod(T input); + Task AsyncMethod(); + void ThrowException(); + } +} diff --git a/src/JKang.IpcServiceFramework.Testing/IpcApplicationFactory.cs b/src/JKang.IpcServiceFramework.Testing/IpcApplicationFactory.cs new file mode 100644 index 0000000..6460f9b --- /dev/null +++ b/src/JKang.IpcServiceFramework.Testing/IpcApplicationFactory.cs @@ -0,0 +1,78 @@ +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Moq; +using System; + +namespace JKang.IpcServiceFramework.Testing +{ + public class IpcApplicationFactory : IDisposable + where TContract : class + { + private Func _serviceFactory = _ => new Mock().Object; + private Action _ipcHostConfig = _ => { }; + private IHost _host = null; + private bool _isDisposed = false; + + public IpcApplicationFactory WithServiceImplementation(TContract serviceInstance) + { + _serviceFactory = _ => serviceInstance; + return this; + } + + public IpcApplicationFactory WithServiceImplementation(Func serviceFactory) + { + _serviceFactory = serviceFactory; + return this; + } + + public IpcApplicationFactory WithIpcHostConfiguration(Action ipcHostConfig) + { + _ipcHostConfig = ipcHostConfig; + return this; + } + + public IIpcClient CreateClient(Action clientConfig) + { + if (clientConfig is null) + { + throw new ArgumentNullException(nameof(clientConfig)); + } + + _host = Host.CreateDefaultBuilder() + .ConfigureServices(x => x.TryAddScoped(_serviceFactory)) + .ConfigureIpcHost(_ipcHostConfig) + .Build(); + + _host.StartAsync().Wait(); + + var services = new ServiceCollection(); + clientConfig.Invoke(services); + + return services.BuildServiceProvider() + .GetRequiredService>(); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + + if (disposing) + { + _host?.Dispose(); + } + + _isDisposed = true; + } + } +} diff --git a/src/JKang.IpcServiceFramework.Testing/JKang.IpcServiceFramework.Testing.csproj b/src/JKang.IpcServiceFramework.Testing/JKang.IpcServiceFramework.Testing.csproj new file mode 100644 index 0000000..3c197f3 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Testing/JKang.IpcServiceFramework.Testing.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + + + diff --git a/src/JKang.IpcServiceFramework.Testing/TestBase.cs b/src/JKang.IpcServiceFramework.Testing/TestBase.cs new file mode 100644 index 0000000..f4e7001 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Testing/TestBase.cs @@ -0,0 +1,23 @@ +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Testing.Fixtures; +using Moq; +using Xunit; + +namespace JKang.IpcServiceFramework.Testing +{ + public abstract class TestBase + : IClassFixture> + { + protected TestBase(IpcApplicationFactory factory) + { + Factory = factory.WithServiceImplementation(ServiceMock.Object); + } + + protected Mock ServiceMock => new Mock(); + + protected IpcApplicationFactory Factory { get; } + + protected abstract IIpcClient CreateClient( + IpcApplicationFactory factory); + } +} diff --git a/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs b/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs new file mode 100644 index 0000000..3f927b0 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs @@ -0,0 +1,148 @@ +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Testing.Fixtures; +using Moq; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Numerics; +using System.Threading.Tasks; +using Xunit; + +namespace JKang.IpcServiceFramework.Testing.TestCases +{ + public class ContractTestCase + { + private readonly Mock _serviceMock; + private readonly IIpcClient _client; + + public ContractTestCase( + Mock serviceMock, + IIpcClient client) + { + _serviceMock = serviceMock; + _client = client; + } + + public async Task PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g, int h, uint i, + long j, ulong k, short l, ushort m, int expected) + { + _serviceMock + .Setup(x => x.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m)) + .Returns(expected); + + int actual = await _client + .InvokeAsync(x => x.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m)); + + Assert.Equal(expected, actual); + } + + public async Task StringType(string input, string expected) + { + _serviceMock + .Setup(x => x.StringType(input)) + .Returns(expected); + + string actual = await _client + .InvokeAsync(x => x.StringType(input)); + + Assert.Equal(expected, actual); + } + + public async Task ComplexType(Complex input, Complex expected) + { + _serviceMock.Setup(x => x.ComplexType(input)).Returns(expected); + + Complex actual = await _client + .InvokeAsync(x => x.ComplexType(input)); + + Assert.Equal(expected, actual); + } + + public async Task ComplexTypeArray(IEnumerable input, IEnumerable expected) + { + _serviceMock + .Setup(x => x.ComplexTypeArray(input)) + .Returns(expected); + + IEnumerable actual = await _client + .InvokeAsync(x => x.ComplexTypeArray(input)); + + Assert.Equal(expected, actual); + } + + public async Task ReturnVoid() + { + await _client.InvokeAsync(x => x.ReturnVoid()); + } + + public async Task DateTime(DateTime input, DateTime expected) + { + _serviceMock.Setup(x => x.DateTime(input)).Returns(expected); + + DateTime actual = await _client + .InvokeAsync(x => x.DateTime(input)); + + Assert.Equal(expected, actual); + } + + public async Task EnumType(DateTimeStyles input, DateTimeStyles expected) + { + _serviceMock.Setup(x => x.EnumType(input)).Returns(expected); + + DateTimeStyles actual = await _client + .InvokeAsync(x => x.EnumType(input)); + + Assert.Equal(expected, actual); + } + + public async Task ByteArray(byte[] input, byte[] expected) + { + _serviceMock.Setup(x => x.ByteArray(input)).Returns(expected); + + byte[] actual = await _client + .InvokeAsync(x => x.ByteArray(input)); + + Assert.Equal(expected, actual); + } + + public async Task GenericMethod(decimal input, decimal expected) + { + _serviceMock + .Setup(x => x.GenericMethod(input)) + .Returns(expected); + + decimal actual = await _client + .InvokeAsync(x => x.GenericMethod(input)); + + Assert.Equal(expected, actual); + } + + public async Task AsyncMethod(int expected) + { + _serviceMock + .Setup(x => x.AsyncMethod()) + .ReturnsAsync(expected); + + int actual = await _client + .InvokeAsync(x => x.AsyncMethod()); + + Assert.Equal(expected, actual); + } + + public async Task ThrowException(Exception expected) + { + _serviceMock + .Setup(x => x.ThrowException()) + .Throws(expected); + + IpcServerUserCodeException exception = await Assert.ThrowsAsync(async () => + { + await _client + .InvokeAsync(x => x.ThrowException()); + }); + + Assert.Contains(expected.Message, exception.Message); + Assert.DoesNotContain(IpcServerException.ServerFailureDetails, exception.ToString()); + } + } +} From c05b23aba89ac5cde17d99c2c0bd69ff17f05bbc Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 25 Apr 2020 17:16:51 +0200 Subject: [PATCH 02/53] ci: update travis script --- .travis.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 145563d..a7141b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,11 @@ language: csharp mono: none +dotnet: 3.1 +dist: xenial solution: IpcServiceFramework.sln -dotnet: 2.1.502 -dist: trusty -sudo: required +install: + - cd ./src + - dotnet restore script: - cd ./src - dotnet restore From 453f87acae685c9638da6626c2992e39f0f016b7 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 25 Apr 2020 19:52:58 +0200 Subject: [PATCH 03/53] try to fix travis --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index a7141b9..edef747 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,9 @@ language: csharp mono: none -dotnet: 3.1 -dist: xenial solution: IpcServiceFramework.sln -install: - - cd ./src - - dotnet restore +dotnet: 3.1.201 +dist: xenial +sudo: required script: - cd ./src - dotnet restore From 0a75d1ad2af0039fc3e37b91c77e79ddf0e56967 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 25 Apr 2020 20:03:50 +0200 Subject: [PATCH 04/53] add workflow file --- .github/workflows/dotnetcore.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/workflows/dotnetcore.yml diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml new file mode 100644 index 0000000..e69de29 From 6538eebd9d97bb741278d58cf4a982df23d1bacf Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 25 Apr 2020 20:13:45 +0200 Subject: [PATCH 05/53] Set up CI with Azure Pipelines [skip ci] --- azure-pipelines.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..60dbc22 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,34 @@ +# ASP.NET Core (.NET Framework) +# Build and test ASP.NET Core projects targeting the full .NET Framework. +# Add steps that publish symbols, save build artifacts, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core + +trigger: +- develop + +pool: + vmImage: 'windows-latest' + +variables: + solution: '**/*.sln' + buildPlatform: 'Any CPU' + buildConfiguration: 'Release' + +steps: +- task: NuGetToolInstaller@1 + +- task: NuGetCommand@2 + inputs: + restoreSolution: '$(solution)' + +- task: VSBuild@1 + inputs: + solution: '$(solution)' + msbuildArgs: '' + platform: '$(buildPlatform)' + configuration: '$(buildConfiguration)' + +- task: VSTest@2 + inputs: + platform: '$(buildPlatform)' + configuration: '$(buildConfiguration)' From 08ed40379951dd4b193569fb6ccefba54807de01 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 25 Apr 2020 20:29:31 +0200 Subject: [PATCH 06/53] ci: remove travis file and action file --- .github/workflows/dotnetcore.yml | 0 .travis.yml | 11 ----------- src/IpcServiceFramework.sln | 1 - 3 files changed, 12 deletions(-) delete mode 100644 .github/workflows/dotnetcore.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml deleted file mode 100644 index e69de29..0000000 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index edef747..0000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: csharp -mono: none -solution: IpcServiceFramework.sln -dotnet: 3.1.201 -dist: xenial -sudo: required -script: - - cd ./src - - dotnet restore - - dotnet build --configuration Release - - dotnet test JKang.IpcServiceFramework.Core.Tests diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index 84f46ed..54b9220 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -6,7 +6,6 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20913218-C740-42E9-9D17-CAD973B676D0}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - ..\.travis.yml = ..\.travis.yml Directory.Build.props = Directory.Build.props ..\README.md = ..\README.md EndProjectSection From 863cd18a005857a0863b71330abc5dd64eb03c9a Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 14:19:42 +0200 Subject: [PATCH 07/53] Set up CI with Azure Pipelines [skip ci] --- azure-pipelines-ci.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 azure-pipelines-ci.yml diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml new file mode 100644 index 0000000..ff9fa8d --- /dev/null +++ b/azure-pipelines-ci.yml @@ -0,0 +1,35 @@ +# ASP.NET Core (.NET Framework) +# Build and test ASP.NET Core projects targeting the full .NET Framework. +# Add steps that publish symbols, save build artifacts, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core + +trigger: +- develop + +pool: + vmImage: 'ubuntu-16.04' + +variables: + BuildConfiguration: 'Release' + +steps: +- task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: build + projects: '**/*.sln' + arguments: '--configuration $(BuildConfiguration)' + +- task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: test + projects: '**/*Tests/*.csproj' + arguments: '--configuration $(BuildConfiguration)' + +- task: DotNetCoreCLI@2 + displayName: Pack + inputs: + command: pack + projects: '**/*.sln' + outputDir: '$(Build.ArtifactStagingDirectory)' From f6753ef0f050ca1eff063b5a67c814c110b0db60 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 14:22:21 +0200 Subject: [PATCH 08/53] ci: fix failing pack task --- azure-pipelines-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index ff9fa8d..1632995 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -32,4 +32,5 @@ steps: inputs: command: pack projects: '**/*.sln' + arguments: '--configuration $(BuildConfiguration)' outputDir: '$(Build.ArtifactStagingDirectory)' From 544fb28e8d006c4f9021402131abf6203b1ec448 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 14:35:43 +0200 Subject: [PATCH 09/53] ci: update pipeline definitions --- azure-pipelines-ci.yml | 7 +++++-- azure-pipelines.yml => azure-pipelines-pr.yml | 3 ++- src/IpcServiceFramework.sln | 12 +++++++----- 3 files changed, 14 insertions(+), 8 deletions(-) rename azure-pipelines.yml => azure-pipelines-pr.yml (98%) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 1632995..d6e3682 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -3,8 +3,11 @@ # Add steps that publish symbols, save build artifacts, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core +name: 3.0.0$(Rev:.r) + trigger: - develop +- master pool: vmImage: 'ubuntu-16.04' @@ -17,7 +20,7 @@ steps: displayName: Build inputs: command: build - projects: '**/*.sln' + projects: '**/IpcServiceFramework.sln' arguments: '--configuration $(BuildConfiguration)' - task: DotNetCoreCLI@2 @@ -31,6 +34,6 @@ steps: displayName: Pack inputs: command: pack - projects: '**/*.sln' + projects: '**/IpcServiceFramework.sln' arguments: '--configuration $(BuildConfiguration)' outputDir: '$(Build.ArtifactStagingDirectory)' diff --git a/azure-pipelines.yml b/azure-pipelines-pr.yml similarity index 98% rename from azure-pipelines.yml rename to azure-pipelines-pr.yml index d1cdf3a..81fb2e7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines-pr.yml @@ -3,8 +3,9 @@ # Add steps that publish symbols, save build artifacts, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core -trigger: +pr: - develop +- master pool: vmImage: 'ubuntu-16.04' diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index 54b9220..811bb31 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -6,6 +6,8 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20913218-C740-42E9-9D17-CAD973B676D0}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + ..\azure-pipelines-ci.yml = ..\azure-pipelines-ci.yml + ..\azure-pipelines-pr.yml = ..\azure-pipelines-pr.yml Directory.Build.props = Directory.Build.props ..\README.md = ..\README.md EndProjectSection @@ -18,15 +20,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.H EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NamedPipe", "NamedPipe", "{7ED117EC-C390-4D2E-94D3-C00010B63535}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Client.NamedPipe", "JKang.IpcServiceFramework.Client.NamedPipe\JKang.IpcServiceFramework.Client.NamedPipe.csproj", "{3BCFFC74-2722-43DC-B910-92A34997FA2B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client.NamedPipe", "JKang.IpcServiceFramework.Client.NamedPipe\JKang.IpcServiceFramework.Client.NamedPipe.csproj", "{3BCFFC74-2722-43DC-B910-92A34997FA2B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tcp", "Tcp", "{7A7B0FCA-ADE5-4C7A-9640-9E10821D251C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Hosting.Tcp", "JKang.IpcServiceFramework.Hosting.Tcp\JKang.IpcServiceFramework.Hosting.Tcp.csproj", "{2783640E-5E3D-447A-BEC2-C1A74E09089C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Hosting.Tcp", "JKang.IpcServiceFramework.Hosting.Tcp\JKang.IpcServiceFramework.Hosting.Tcp.csproj", "{2783640E-5E3D-447A-BEC2-C1A74E09089C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Client.Tcp", "JKang.IpcServiceFramework.Client.Tcp\JKang.IpcServiceFramework.Client.Tcp.csproj", "{E1EE30C2-EF31-47C9-B9FC-D55A94911C40}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client.Tcp", "JKang.IpcServiceFramework.Client.Tcp\JKang.IpcServiceFramework.Client.Tcp.csproj", "{E1EE30C2-EF31-47C9-B9FC-D55A94911C40}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Abstractions", "JKang.IpcServiceFramework.Abstractions\JKang.IpcServiceFramework.Abstractions.csproj", "{F8E7A89B-F863-42FF-9FE0-77A107938454}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Abstractions", "JKang.IpcServiceFramework.Abstractions\JKang.IpcServiceFramework.Abstractions.csproj", "{F8E7A89B-F863-42FF-9FE0-77A107938454}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client", "JKang.IpcServiceFramework.Client\JKang.IpcServiceFramework.Client.csproj", "{46465A56-CF19-4403-A78C-9871BFD8B49E}" EndProject @@ -36,7 +38,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.H EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client.Abstractions", "JKang.IpcServiceFramework.Client.Abstractions\JKang.IpcServiceFramework.Client.Abstractions.csproj", "{AE3DE1F9-EAF2-4785-9A23-E03F40D52156}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JKang.IpcServiceFramework.Testing", "JKang.IpcServiceFramework.Testing\JKang.IpcServiceFramework.Testing.csproj", "{915000BD-9D84-4554-9A91-428923060EC5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Testing", "JKang.IpcServiceFramework.Testing\JKang.IpcServiceFramework.Testing.csproj", "{915000BD-9D84-4554-9A91-428923060EC5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.TcpTests", "JKang.IpcServiceFramework.TcpTests\JKang.IpcServiceFramework.TcpTests.csproj", "{C6625102-AC1B-4D86-8BF8-B2096F701AC2}" EndProject From 5e17cd6aaef6bbc789df925a53cff163bea70472 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 14:51:47 +0200 Subject: [PATCH 10/53] ci: try to fix ci build --- azure-pipelines-ci.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index d6e3682..17e25e5 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -3,11 +3,10 @@ # Add steps that publish symbols, save build artifacts, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core -name: 3.0.0$(Rev:.r) +name: 3.0.0$(Rev:.r)-alpha trigger: - develop -- master pool: vmImage: 'ubuntu-16.04' @@ -19,21 +18,21 @@ steps: - task: DotNetCoreCLI@2 displayName: Build inputs: - command: build - projects: '**/IpcServiceFramework.sln' + command: 'build' + projects: 'src/*.sln' arguments: '--configuration $(BuildConfiguration)' - task: DotNetCoreCLI@2 displayName: Test inputs: - command: test - projects: '**/*Tests/*.csproj' + command: 'test' + projects: 'src/*Tests/*.csproj' arguments: '--configuration $(BuildConfiguration)' - task: DotNetCoreCLI@2 displayName: Pack inputs: - command: pack - projects: '**/IpcServiceFramework.sln' - arguments: '--configuration $(BuildConfiguration)' - outputDir: '$(Build.ArtifactStagingDirectory)' + command: 'pack' + packagesToPack: 'src/*.csproj' + nobuild: true + versioningScheme: 'byBuildNumber' From 69d422886bcd57d0c0322ca029c568921517ec1d Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 14:55:47 +0200 Subject: [PATCH 11/53] ci: fix ci build again --- azure-pipelines-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 17e25e5..177e272 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -33,6 +33,6 @@ steps: displayName: Pack inputs: command: 'pack' - packagesToPack: 'src/*.csproj' + packagesToPack: 'src/**/*.csproj' nobuild: true versioningScheme: 'byBuildNumber' From 7661664d4531f3b635b6c0b838fda5e79ff23a4f Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 15:00:56 +0200 Subject: [PATCH 12/53] ci: update pipeline definitions --- azure-pipelines-ci.yml | 6 +++--- azure-pipelines-pr.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 177e272..5183e39 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -12,7 +12,7 @@ pool: vmImage: 'ubuntu-16.04' variables: - BuildConfiguration: 'Release' + buildConfiguration: 'Release' steps: - task: DotNetCoreCLI@2 @@ -20,14 +20,14 @@ steps: inputs: command: 'build' projects: 'src/*.sln' - arguments: '--configuration $(BuildConfiguration)' + arguments: '--configuration $(buildConfiguration)' - task: DotNetCoreCLI@2 displayName: Test inputs: command: 'test' projects: 'src/*Tests/*.csproj' - arguments: '--configuration $(BuildConfiguration)' + arguments: '--configuration $(buildConfiguration)' - task: DotNetCoreCLI@2 displayName: Pack diff --git a/azure-pipelines-pr.yml b/azure-pipelines-pr.yml index 81fb2e7..048937c 100644 --- a/azure-pipelines-pr.yml +++ b/azure-pipelines-pr.yml @@ -11,7 +11,7 @@ pool: vmImage: 'ubuntu-16.04' variables: - buildConfiguration: 'Release' + BuildConfiguration: 'Release' steps: - task: DotNetCoreCLI@2 From 530dac3102ea19fd86544d136e4886cfe9817b3d Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 15:17:12 +0200 Subject: [PATCH 13/53] ci: do not run ci build for pr, vice versa --- azure-pipelines-ci.yml | 9 ++++++++- azure-pipelines-pr.yml | 10 ++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 5183e39..4062146 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -6,7 +6,14 @@ name: 3.0.0$(Rev:.r)-alpha trigger: -- develop + branches: + include: + - develop + paths: + include: + - src/* + +pr: none pool: vmImage: 'ubuntu-16.04' diff --git a/azure-pipelines-pr.yml b/azure-pipelines-pr.yml index 048937c..dba2d74 100644 --- a/azure-pipelines-pr.yml +++ b/azure-pipelines-pr.yml @@ -3,9 +3,15 @@ # Add steps that publish symbols, save build artifacts, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core +trigger: none + pr: -- develop -- master + branches: + include: + - '*' + paths: + include: + - src/* pool: vmImage: 'ubuntu-16.04' From 5cc81043f4b77d389b776e6ca27896bf0fbb6466 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 15:34:12 +0200 Subject: [PATCH 14/53] ci: remove path filter that prevent build from running when changing build definition files (#97) Co-authored-by: Jacques Kang --- azure-pipelines-ci.yml | 3 --- azure-pipelines-pr.yml | 3 --- 2 files changed, 6 deletions(-) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 4062146..10c7e00 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -9,9 +9,6 @@ trigger: branches: include: - develop - paths: - include: - - src/* pr: none diff --git a/azure-pipelines-pr.yml b/azure-pipelines-pr.yml index dba2d74..9635b03 100644 --- a/azure-pipelines-pr.yml +++ b/azure-pipelines-pr.yml @@ -9,9 +9,6 @@ pr: branches: include: - '*' - paths: - include: - - src/* pool: vmImage: 'ubuntu-16.04' From 2f8c449ad2c8042470054a79f47478c989fff519 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 15:37:50 +0200 Subject: [PATCH 15/53] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e4618d..3dc2b73 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/jacqueskang/IpcServiceFramework.svg?branch=develop)](https://travis-ci.org/jacqueskang/IpcServiceFramework) +[![Build Status](https://dev.azure.com/jacques-kang/IpcServiceFramework/_apis/build/status/IpcServiceFramework%20CI?branchName=develop)](https://dev.azure.com/jacques-kang/IpcServiceFramework/_build/latest?definitionId=9&branchName=develop) # IpcServiceFramework From 47548f994a7dc7632f0966817e0e6d3e5b34124e Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 16:21:00 +0200 Subject: [PATCH 16/53] ci: publish build artifacts (nuget packages) --- azure-pipelines-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 10c7e00..28fc3d2 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -40,3 +40,9 @@ steps: packagesToPack: 'src/**/*.csproj' nobuild: true versioningScheme: 'byBuildNumber' + +- task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: 'drop' + publishLocation: 'Container' \ No newline at end of file From 780750212ea9a5c7fc2787bddbc2d5d416dffbc1 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 16:44:40 +0200 Subject: [PATCH 17/53] ci: add -alpha suffix to nuget package version --- azure-pipelines-ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 28fc3d2..145b195 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -3,7 +3,7 @@ # Add steps that publish symbols, save build artifacts, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core -name: 3.0.0$(Rev:.r)-alpha +name: 3.0.0$(Rev:.r) trigger: branches: @@ -17,6 +17,7 @@ pool: variables: buildConfiguration: 'Release' + nugetVersion: '$(Build.BuildNumber)-alpha' steps: - task: DotNetCoreCLI@2 @@ -39,7 +40,8 @@ steps: command: 'pack' packagesToPack: 'src/**/*.csproj' nobuild: true - versioningScheme: 'byBuildNumber' + versioningScheme: 'byEnvVar' + versionEnvVar: 'nugetVersion' - task: PublishBuildArtifacts@1 inputs: From efd068abd4c9e275f5a4d26e26f1a079f20b55ba Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 16:52:52 +0200 Subject: [PATCH 18/53] Feature/ci (#98) * ci: remove path filter that prevent build from running when changing build definition files * ci: update dll version and package tags Co-authored-by: Jacques Kang --- src/Directory.Build.props | 4 +--- .../JKang.IpcServiceFramework.Abstractions.csproj | 1 + .../JKang.IpcServiceFramework.Client.Abstractions.csproj | 1 + .../JKang.IpcServiceFramework.Client.NamedPipe.csproj | 1 + .../JKang.IpcServiceFramework.Client.Tcp.csproj | 1 + .../JKang.IpcServiceFramework.Client.csproj | 2 +- .../JKang.IpcServiceFramework.Core.csproj | 2 +- .../JKang.IpcServiceFramework.Hosting.Abstractions.csproj | 1 + .../JKang.IpcServiceFramework.Hosting.NamedPipe.csproj | 1 + .../JKang.IpcServiceFramework.Hosting.Tcp.csproj | 1 + .../JKang.IpcServiceFramework.Hosting.csproj | 1 + 11 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7ab2261..dddcd4a 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -5,10 +5,8 @@ https://github.com/jacqueskang/IpcServiceFramework/blob/develop/LICENSE https://github.com/jacqueskang/IpcServiceFramework - dotnetcore,named-pipes,interprocess-communication https://github.com/jacqueskang/IpcServiceFramework - - 2.0.3 + 3.0.0 \ No newline at end of file diff --git a/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj b/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj index 67943b7..bebff63 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj +++ b/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj @@ -3,6 +3,7 @@ netstandard2.0 JKang.IpcServiceFramework + ipc,interprocess,communication,wcf diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj b/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj index 757361c..510db61 100644 --- a/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj +++ b/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj @@ -3,6 +3,7 @@ netstandard2.0 JKang.IpcServiceFramework.Client + ipc,interprocess,communication,wcf,client diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/JKang.IpcServiceFramework.Client.NamedPipe.csproj b/src/JKang.IpcServiceFramework.Client.NamedPipe/JKang.IpcServiceFramework.Client.NamedPipe.csproj index bb2d870..689ddee 100644 --- a/src/JKang.IpcServiceFramework.Client.NamedPipe/JKang.IpcServiceFramework.Client.NamedPipe.csproj +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/JKang.IpcServiceFramework.Client.NamedPipe.csproj @@ -2,6 +2,7 @@ netstandard2.0 + ipc,interprocess,communication,wcf,client,namedpipe diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/JKang.IpcServiceFramework.Client.Tcp.csproj b/src/JKang.IpcServiceFramework.Client.Tcp/JKang.IpcServiceFramework.Client.Tcp.csproj index bb2d870..7f6228f 100644 --- a/src/JKang.IpcServiceFramework.Client.Tcp/JKang.IpcServiceFramework.Client.Tcp.csproj +++ b/src/JKang.IpcServiceFramework.Client.Tcp/JKang.IpcServiceFramework.Client.Tcp.csproj @@ -2,6 +2,7 @@ netstandard2.0 + ipc,interprocess,communication,wcf,client,tcp diff --git a/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj b/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj index d8318cc..abdf38b 100644 --- a/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj +++ b/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj @@ -3,7 +3,7 @@ netstandard2.0 JKang.IpcServiceFramework - true + ipc,interprocess,communication,wcf,client diff --git a/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj b/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj index 1b18978..205ae55 100644 --- a/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj +++ b/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj @@ -3,7 +3,7 @@ netstandard2.0 JKang.IpcServiceFramework - true + ipc,interprocess,communication,wcf diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj b/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj index e6757ff..969562a 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj @@ -3,6 +3,7 @@ netstandard2.0 JKang.IpcServiceFramework.Hosting + ipc,interprocess,communication,wcf,hosting diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj index 6e9ab67..85172f4 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj @@ -2,6 +2,7 @@ netstandard2.0 + ipc,interprocess,communication,wcf,hosting,namedpipe diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/JKang.IpcServiceFramework.Hosting.Tcp.csproj b/src/JKang.IpcServiceFramework.Hosting.Tcp/JKang.IpcServiceFramework.Hosting.Tcp.csproj index 61c2952..978ab2e 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Tcp/JKang.IpcServiceFramework.Hosting.Tcp.csproj +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/JKang.IpcServiceFramework.Hosting.Tcp.csproj @@ -2,6 +2,7 @@ netstandard2.0 + ipc,interprocess,communication,wcf,hosting,tcp diff --git a/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj b/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj index 580c045..0684922 100644 --- a/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj +++ b/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj @@ -2,6 +2,7 @@ netstandard2.0 + ipc,interprocess,communication,wcf,hosting From 3afadb222301e6e64fb402b36682d9ed94c72a74 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 26 Apr 2020 20:18:44 +0200 Subject: [PATCH 19/53] Update README.md --- README.md | 82 +++++++++++++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 3dc2b73..c56b521 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,8 @@ # IpcServiceFramework -A .NET Core lightweight inter-process communication framework allowing invoking a service via named pipeline and/or TCP (in a similar way as WCF, which is currently unavailable for .NET Core). Secure communication over SSL is also supported. - -Support using primitive or complex types in service contract. - -Support multi-threading on server side with configurable number of threads (named pipeline endpoint only). - -[ASP.NET Core Dependency Injection framework](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection) friendly. +A .NET Core 3.1 based lightweight framework for efficient inter-process communication. +Named pipeline and TCP support out-of-the-box, extensible with other protocols. ## Usage 1. Create an interface as service contract and package it in an assembly to be shared between server and client. @@ -19,37 +14,41 @@ Support multi-threading on server side with configurable number of threads (name IpcServiceFramework is available via NuGet: - - [JKang.IpcServiceFramework.Server](https://www.nuget.org/packages/JKang.IpcServiceFramework.Server/) - - [JKang.IpcServiceFramework.Client](https://www.nuget.org/packages/JKang.IpcServiceFramework.Client/) + - [JKang.IpcServiceFramework.Hosting.NamedPipe](https://www.nuget.org/packages/JKang.IpcServiceFramework.Hosting.NamedPipe/) + - [JKang.IpcServiceFramework.Client.NamedPipe](https://www.nuget.org/packages/JKang.IpcServiceFramework.Client.NamedPipe/) + - [JKang.IpcServiceFramework.Hosting.Tcp](https://www.nuget.org/packages/JKang.IpcServiceFramework.Hosting.Tcp/) + - [JKang.IpcServiceFramework.Client.NamedPipe](https://www.nuget.org/packages/JKang.IpcServiceFramework.Client.NamedPipe/) ## Quick Start: ### Step 1: Create service contract ```csharp - public interface IComputingService + public interface IInterProcessService { - float AddFloat(float x, float y); + string ReverseString(string input); } ``` _Note: This interface is ideally to be placed in a library assembly to be shared between server and client._ ### Step 2: Implement the server -1. Create a console application with the following 2 NuGet packages installed: +1. Create a console application with the following NuGet packages installed: ``` -> Install-Package Microsoft.Extensions.DependencyInjection -> Install-Package JKang.IpcServiceFramework.Server +> Install-Package Microsoft.Extensions.Hosting +> Install-Package JKang.IpcServiceFramework.Hosting.NamedPipe ``` 2. Add an class that implements the service contract ```csharp - class ComputingService : IComputingService + class InterProcessService : IInterProcessService { - public float AddFloat(float x, float y) + public string ReverseString(string input) { - return x + y; + char[] charArray = input.ToCharArray(); + Array.Reverse(input.ToCharArray()); + return new string(charArray); } } ``` @@ -59,53 +58,40 @@ _Note: This interface is ideally to be placed in a library assembly to be shared ```csharp class Program { - static void Main(string[] args) + public static void Main(string[] args) { - // configure DI - IServiceCollection services = ConfigureServices(new ServiceCollection()); - - // build and run service host - new IpcServiceHostBuilder(services.BuildServiceProvider()) - .AddNamedPipeEndpoint(name: "endpoint1", pipeName: "pipeName") - .AddTcpEndpoint(name: "endpoint2", ipEndpoint: IPAddress.Loopback, port: 45684) - .Build() - .Run(); + CreateHostBuilder(args).Build().Run(); } - private static IServiceCollection ConfigureServices(IServiceCollection services) - { - return services - .AddIpc(builder => + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureServices(services => { - builder - .AddNamedPipe(options => - { - options.ThreadCount = 2; - }) - .AddService(); + services.AddScoped(); + }) + .ConfigureIpcHost(builder => + { + builder.AddNamedPipeEndpoint("endpoint1", "pipeinternal"); }); - } } ``` -_Note: It's possible to host IPC service in web application, please check out the sample project *IpcServiceSample.WebServer*_ ### Step 3: Invoke the service from client process 1. Install the following package in client application: -```bash -$ dotnet add package JKang.IpcServiceFramework.Client +``` +> Install-Package JKang.IpcServiceFramework.Client.NamedPipe ``` -2. Add reference to the assembly created in step 1 which contains `IComputingService` interface. - -3. Invoke the server +2. Invoke the server ```csharp - IpcServiceClient client = new IpcServiceClientBuilder() - .UseNamedPipe("pipeName") // or .UseTcp(IPAddress.Loopback, 45684) to invoke using TCP - .Build(); + IIpcClient client = new ServiceCollection() + .AddNamedPipeIpcClient("pipeinternal") + .BuildServiceProvider() + .GetRequiredService>(); - float result = await client.InvokeAsync(x => x.AddFloat(1.23f, 4.56f)); + string output = await client.InvokeAsync(x => x.ReverseString(input)); ``` __Welcome to raise any issue or even provide any suggestion/PR to participate this project!__ From 365a43b42b362148fa09b9da55281b7b34bdef7e Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Mon, 27 Apr 2020 11:42:36 +0200 Subject: [PATCH 20/53] Refactor/graceful shutdown (#99) * refactor: update sample project * refactor: simplify IpcHostedService by inheriting from BackgroundService * refactor: fix failing build Co-authored-by: Jacques Kang --- .../IpcServiceSample.ConsoleClient.csproj | 17 +++ samples/IpcServiceSample.Client/Program.cs | 25 +++ .../IpcServiceSample.ConsoleClient/Program.cs | 143 ------------------ .../Certificates/cert.crt | 32 ---- .../Certificates/key.pem | 52 ------- .../ComputingService.cs | 50 ------ .../IpcServiceSample.ConsoleServer.csproj | 24 --- .../IpcServiceSample.ConsoleServer/Program.cs | 93 ------------ .../SystemService.cs | 47 ------ .../TestService.cs | 16 -- .../InterProcessService.cs | 15 ++ .../IpcServiceSample.Server.csproj} | 8 +- samples/IpcServiceSample.Server/Program.cs | 27 ++++ .../Helpers/LoggingStream.cs | 105 ------------- .../Helpers/XorStream.cs | 58 ------- .../IComputingService.cs | 34 ----- .../IInterProcessService.cs | 7 + .../ISystemService.cs | 14 -- .../ITestService.cs | 11 -- .../IpcServiceSample.ServiceContracts.csproj | 1 - .../Certificates/cert.crt | 32 ---- .../Certificates/key.pem | 52 ------- .../IpcServiceSample.WebServer.csproj | 23 --- samples/IpcServiceSample.WebServer/Program.cs | 41 ----- samples/IpcServiceSample.WebServer/Startup.cs | 40 ----- samples/IpcServiceSample.sln | 37 +++++ samples/Samples/Samples.sln | 13 -- .../IpcRequest.cs | 6 +- .../IIpcEndpoint.cs | 9 +- .../NamedPipeIpcServiceEndpoint.cs | 12 +- .../TcpIpcEndpoint.cs | 24 ++- .../IpcEndpoint.cs | 96 ++++++------ .../IpcHostedService.cs | 19 +-- 33 files changed, 232 insertions(+), 951 deletions(-) create mode 100644 samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj create mode 100644 samples/IpcServiceSample.Client/Program.cs delete mode 100644 samples/IpcServiceSample.ConsoleClient/Program.cs delete mode 100644 samples/IpcServiceSample.ConsoleServer/Certificates/cert.crt delete mode 100644 samples/IpcServiceSample.ConsoleServer/Certificates/key.pem delete mode 100644 samples/IpcServiceSample.ConsoleServer/ComputingService.cs delete mode 100644 samples/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj delete mode 100644 samples/IpcServiceSample.ConsoleServer/Program.cs delete mode 100644 samples/IpcServiceSample.ConsoleServer/SystemService.cs delete mode 100644 samples/IpcServiceSample.ConsoleServer/TestService.cs create mode 100644 samples/IpcServiceSample.Server/InterProcessService.cs rename samples/{IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj => IpcServiceSample.Server/IpcServiceSample.Server.csproj} (52%) create mode 100644 samples/IpcServiceSample.Server/Program.cs delete mode 100644 samples/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs delete mode 100644 samples/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs delete mode 100644 samples/IpcServiceSample.ServiceContracts/IComputingService.cs create mode 100644 samples/IpcServiceSample.ServiceContracts/IInterProcessService.cs delete mode 100644 samples/IpcServiceSample.ServiceContracts/ISystemService.cs delete mode 100644 samples/IpcServiceSample.ServiceContracts/ITestService.cs delete mode 100644 samples/IpcServiceSample.WebServer/Certificates/cert.crt delete mode 100644 samples/IpcServiceSample.WebServer/Certificates/key.pem delete mode 100644 samples/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj delete mode 100644 samples/IpcServiceSample.WebServer/Program.cs delete mode 100644 samples/IpcServiceSample.WebServer/Startup.cs create mode 100644 samples/IpcServiceSample.sln delete mode 100644 samples/Samples/Samples.sln diff --git a/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj b/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj new file mode 100644 index 0000000..02e4e38 --- /dev/null +++ b/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/IpcServiceSample.Client/Program.cs b/samples/IpcServiceSample.Client/Program.cs new file mode 100644 index 0000000..b590bbd --- /dev/null +++ b/samples/IpcServiceSample.Client/Program.cs @@ -0,0 +1,25 @@ +using IpcServiceSample.ServiceContracts; +using JKang.IpcServiceFramework.Client; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Threading.Tasks; + +namespace IpcServiceSample.ConsoleClient +{ + class Program + { + private static async Task Main(string[] args) + { + Console.WriteLine("Type a phrase and press enter:"); + string input = Console.ReadLine(); + + IIpcClient client = new ServiceCollection() + .AddNamedPipeIpcClient("pipeinternal") + .BuildServiceProvider() + .GetRequiredService>(); + + string output = await client.InvokeAsync(x => x.ReverseString(input)); + Console.WriteLine(output); + } + } +} diff --git a/samples/IpcServiceSample.ConsoleClient/Program.cs b/samples/IpcServiceSample.ConsoleClient/Program.cs deleted file mode 100644 index dfb0de9..0000000 --- a/samples/IpcServiceSample.ConsoleClient/Program.cs +++ /dev/null @@ -1,143 +0,0 @@ -using IpcServiceSample.ServiceContracts; -using IpcServiceSample.ServiceContracts.Helpers; -using JKang.IpcServiceFramework; -using System; -using System.Net; -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace IpcServiceSample.ConsoleClient -{ - class Program - { - static void Main(string[] args) - { - MainAsync(args).Wait(); - } - - private static async Task MainAsync(string[] args) - { - try - { - Console.WriteLine("Press any key to cancel."); - var source = new CancellationTokenSource(); - - await Task.WhenAll(RunTestsAsync(source.Token), Task.Run(() => - { - Console.ReadKey(); - source.Cancel(); - })); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - } - - private static bool InsecureValidationCallback_TESTONLY(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { - /* WARNING: Using certificate validation callback can be dangerous. Incorrect implementation can lead to serious security issues. - * - * For example, unconditionally returning true in this function provides no security whatsoever against an attacker who can perform - * man-in-the-middle attacks. - * - * This function is used only for test purposes. It validates only that the correct server certificate is used by the server. - * However, it does not validate the certificate chain or validate that the certificate common name matches the server domain name. - * Do not use this example in a production application. - */ - return certificate.GetCertHashString() == "FA54627C36D3DAEFF69E04B59120992305A7104F"; - } - - private static async Task RunTestsAsync(CancellationToken cancellationToken) - { - IpcServiceClient computingClient = new IpcServiceClientBuilder() - .UseNamedPipe("pipeName") - .Build(); - - IpcServiceClient systemClient = new IpcServiceClientBuilder() - .UseTcp(IPAddress.Loopback, 45684) - .Build(); - - IpcServiceClient secureClient = new IpcServiceClientBuilder() - .UseTcp(IPAddress.Loopback, 44384, "test-ipcsf-secure-server", InsecureValidationCallback_TESTONLY) - .Build(); - - IpcServiceClient xorTranslatedClient = new IpcServiceClientBuilder() - .UseTcp(IPAddress.Loopback, 45454, s => new XorStream(s)) - .Build(); - - IpcServiceClient loggedClient = new IpcServiceClientBuilder() - .UseTcp(IPAddress.Loopback, 45684, s => new LoggingStream(s, "ipc.log")) - .Build(); - - // test 1: call IPC service method with primitive types - float result1 = await computingClient.InvokeAsync(x => x.AddFloat(1.23f, 4.56f), cancellationToken); - Console.WriteLine($"[TEST 1] sum of 2 floating number is: {result1}"); - - // test 2: call IPC service method with complex types - ComplexNumber result2 = await computingClient.InvokeAsync(x => x.AddComplexNumber( - new ComplexNumber(0.1f, 0.3f), - new ComplexNumber(0.2f, 0.6f)), cancellationToken); - Console.WriteLine($"[TEST 2] sum of 2 complexe number is: {result2.A}+{result2.B}i"); - - // test 3: call IPC service method with an array of complex types - ComplexNumber result3 = await computingClient.InvokeAsync(x => x.AddComplexNumbers(new[] - { - new ComplexNumber(0.5f, 0.4f), - new ComplexNumber(0.2f, 0.1f), - new ComplexNumber(0.3f, 0.5f), - }), cancellationToken); - Console.WriteLine($"[TEST 3] sum of 3 complexe number is: {result3.A}+{result3.B}i", cancellationToken); - - // test 4: call IPC service method without parameter or return - await systemClient.InvokeAsync(x => x.DoNothing(), cancellationToken); - Console.WriteLine($"[TEST 4] invoked DoNothing()"); - - // test 5: call IPC service method with enum parameter - string text = await systemClient.InvokeAsync(x => x.ConvertText("hEllO woRd!", TextStyle.Upper), cancellationToken); - Console.WriteLine($"[TEST 5] {text}"); - - // test 6: call IPC service method returning GUID - Guid generatedId = await systemClient.InvokeAsync(x => x.GenerateId(), cancellationToken); - Console.WriteLine($"[TEST 6] generated ID is: {generatedId}"); - - // test 7: call IPC service method with byte array - byte[] input = Encoding.UTF8.GetBytes("Test"); - byte[] reversed = await systemClient.InvokeAsync(x => x.ReverseBytes(input), cancellationToken); - Console.WriteLine($"[TEST 7] reversed bytes are: {Convert.ToBase64String(reversed)}"); - - // test 8: call IPC service method with generic parameter - string print = await systemClient.InvokeAsync(x => x.Printout(DateTime.UtcNow), cancellationToken); - Console.WriteLine($"[TEST 8] print out value: {print}"); - - // test 9: call slow IPC service method - await systemClient.InvokeAsync(x => x.SlowOperation(), cancellationToken); - Console.WriteLine($"[TEST 9] Called slow operation"); - - // test 10: call async server method - await computingClient.InvokeAsync(x => x.MethodAsync()); - Console.WriteLine($"[TEST 10] Called async method"); - - // test 11: call async server function - int sum = await computingClient.InvokeAsync(x => x.SumAsync(1, 1)); - Console.WriteLine($"[TEST 11] Called async function: {sum}"); - - // test 12: call secure service method - generatedId = await secureClient.InvokeAsync(x => x.GenerateId(), cancellationToken); - Console.WriteLine($"[TEST 12] Called secure service method, generated ID is: {generatedId}"); - - // test 13 call translated service method - generatedId = await xorTranslatedClient.InvokeAsync(x => x.GenerateId(), cancellationToken); - Console.WriteLine($"[TEST 13] Called translated service method, generated ID is: {generatedId}"); - - // test 14: use a translated stream to log data to a text file - generatedId = await loggedClient.InvokeAsync(x => x.GenerateId(), cancellationToken); - Console.WriteLine($"[TEST 14] Called method using stream translator for logging, generated ID is: {generatedId}"); - - Console.WriteLine("All test finished. Press any key to exit."); - } - } -} diff --git a/samples/IpcServiceSample.ConsoleServer/Certificates/cert.crt b/samples/IpcServiceSample.ConsoleServer/Certificates/cert.crt deleted file mode 100644 index 426a5c9..0000000 --- a/samples/IpcServiceSample.ConsoleServer/Certificates/cert.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFfTCCA2WgAwIBAgIUCTca4HpnnD5ZL3E3YIiM+meBmLYwDQYJKoZIhvcNAQEL -BQAwTjELMAkGA1UEBhMCWFgxDTALBgNVBAgMBFRlc3QxDTALBgNVBAoMBFRlc3Qx -ITAfBgNVBAMMGHRlc3QtaXBjc2Ytc2VjdXJlLXNlcnZlcjAeFw0xODEyMTIyMjQ4 -NDNaFw0yODEyMDkyMjQ4NDNaME4xCzAJBgNVBAYTAlhYMQ0wCwYDVQQIDARUZXN0 -MQ0wCwYDVQQKDARUZXN0MSEwHwYDVQQDDBh0ZXN0LWlwY3NmLXNlY3VyZS1zZXJ2 -ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsQeBCs8OknFXnlz0i -a6ScSF2cS/zKKpExDfhcLlGRhcXwydu1q3SMumvFgOMqKeBs7oM2JT1VqEWC77D6 -Cct+rIAdEw/Km1njYxLjo4buYIwJbNeJT+iZv0sSFJvx+Go15n6QfrFcmVGy52Hs -BkD7SQTSLv9D5UwysNn7i/BcSXZPw272oobYqRKeQQ0aVMCP7f0O1vXcJVVsER0H -YpI0m7BTpIFQbGg8jnjSue0AKQmYyzoroOwW1B7y/lp8W43lCL40ZMiBwZQ6jFFp -OHBB+SxQqzWr6duBxA6F96tlDEXZXCJoOt0nIVXtXcv5q5xpcuiIil21aIVMSw3o -Eo7ccvIm/gTCUnkJ0ahx7DAX1jaTt5Zh8K78z0CXxr5pg+Q/BFdGZ0iaYBtKX+yL -1vcWLusKYhI2IEFgtJXhfjaYm/rhTjS4SCzmAMexWPWJXy/Bu1nh2fSrMMZeWyeu -S/0T60cZav/Pqqat+ReufvxRlpYlq8KfOuVrfbwbYZwR4NyVRDPNhAMgTZngqTbN -5xsqrH1pNspwU8emJOUvH0CWY4/qRr5uJzbNsBS9nbJERAQe0wsPpw8ZOgItmEX/ -X562cjGrCXvaQewh2a17kvFq+eEpR/pMs/JBGT22wFVfof0Sy/Fl0w0hHW4QPM7e -+I9mbNuedK4w6vUSqwCK5knpeQIDAQABo1MwUTAdBgNVHQ4EFgQU3Xxk9XFDaGnZ -zck+or/hA5yRJEswHwYDVR0jBBgwFoAU3Xxk9XFDaGnZzck+or/hA5yRJEswDwYD -VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAVih/7NFQsbjieZZcxqvk -XhfnMMXtJZiHnTS5gFEtbAwuhCYE2yrhXngGKXSQX2XCcuSzvgVysiV/prN+KVHf -APZXiGjOaCJwPHkiCeJ/bub+cLclnm3dNnoHgmKhIZgv7C/bAnGGdcwlOP76lgg2 -c/pEuFyfN1mwEiHjO/ryEHF3KxpGvdlkHZXnSoRP+Ghk0PCmwJkyRaCkn6YbeAq2 -+7CVnlMwzCqsIRb2yz/0COWJYTH3w39Ejf5HGGl2TKQ3mt1425m5j/kJdBxCE6cC -epufzBae143qvkDGN4/3Xhb3QGxh7ep9RGXTgPHlQqXMS6gWpa9dJ1wGJb1M4O92 -V6c+Gp87mA42/sew1kBERx8KupgM/zTatRO+b5DCRkTuZ/aQvxaod3c++0WeufsC -IFMOGkqYNDot2bAqFQKqg72FXR0OD/Iq4R6ZQNeYUoNH3c4ev3zcevPzaO9wwP06 -C5vfp4QivK/3vcjHMxfCEPm3KNls/U6syTTH12RHguGUHsEd7SY4S7n3oJoziXtf -yzKQF+CkDTWC86pjm9bN3AchObYJIbVx+vZnWOzDmOZTE0AshGWxlWTR3TMJLiNr -sgtTfiNEN+6HbKh4aoDAtapwyoGy0LfqoiLHAYjp8ePS1v0Lr80c5yoPDLrfdYHK -ZHQzl1Ey1cFJr0zpx9Bi33I= ------END CERTIFICATE----- diff --git a/samples/IpcServiceSample.ConsoleServer/Certificates/key.pem b/samples/IpcServiceSample.ConsoleServer/Certificates/key.pem deleted file mode 100644 index 1d5c7ec..0000000 --- a/samples/IpcServiceSample.ConsoleServer/Certificates/key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCsQeBCs8OknFXn -lz0ia6ScSF2cS/zKKpExDfhcLlGRhcXwydu1q3SMumvFgOMqKeBs7oM2JT1VqEWC -77D6Cct+rIAdEw/Km1njYxLjo4buYIwJbNeJT+iZv0sSFJvx+Go15n6QfrFcmVGy -52HsBkD7SQTSLv9D5UwysNn7i/BcSXZPw272oobYqRKeQQ0aVMCP7f0O1vXcJVVs -ER0HYpI0m7BTpIFQbGg8jnjSue0AKQmYyzoroOwW1B7y/lp8W43lCL40ZMiBwZQ6 -jFFpOHBB+SxQqzWr6duBxA6F96tlDEXZXCJoOt0nIVXtXcv5q5xpcuiIil21aIVM -Sw3oEo7ccvIm/gTCUnkJ0ahx7DAX1jaTt5Zh8K78z0CXxr5pg+Q/BFdGZ0iaYBtK -X+yL1vcWLusKYhI2IEFgtJXhfjaYm/rhTjS4SCzmAMexWPWJXy/Bu1nh2fSrMMZe -WyeuS/0T60cZav/Pqqat+ReufvxRlpYlq8KfOuVrfbwbYZwR4NyVRDPNhAMgTZng -qTbN5xsqrH1pNspwU8emJOUvH0CWY4/qRr5uJzbNsBS9nbJERAQe0wsPpw8ZOgIt -mEX/X562cjGrCXvaQewh2a17kvFq+eEpR/pMs/JBGT22wFVfof0Sy/Fl0w0hHW4Q -PM7e+I9mbNuedK4w6vUSqwCK5knpeQIDAQABAoICAQCUKFTrChLMEmsQtlQutsbu -ZI+fTvwuJk6bEpj7MBuYPqbxY61FpCKqp+zqAuFf8oTFLKBOgdkvQ3wGEoL1jFcq -rNPELhD3AoddvGkSwiPcA85ujN8Vi1VUZ+P5uSAoDrHLimRxg4apTnWmmrzudLKP -b05mOWX0z9OqBdJ3OPWTatwH3uAh4ch5sXICC5FphFvbb6aojNsKblH6kP2WzIFU -HlSanHNc6OD+tMvW83OVH7bRZHqz68UkHW5BMeRB8b0psUtnZQfQEt+bO/UJuzFS -jS5AdAHFy26xPh//ufYBA31QZp5xZ6+vaEyvzG0UYTY2vE6kod3Xmf6MkEF1ygB0 -0arsKvpTi9DN3tT02oxO1zrGj35Hn5Hmdq4SjexRHTRktlOpY6e7pOYK1fnKxXlX -OVn7+iaFBIM2M1GhXdcuetMd4cswtKbOwR5K4HpO3op64IeiNlh2OdrzkbW+67s0 -g5N3kNZ6wZ4x8Syim8SsEEYHu1bhOvp5Im3zCVdVOxvkYogjxVc+COayu0ROPR4K -81DLX5oI8AV7LkRVNiAH4/c/qRm2eTPdVtrrSBZ+GoSBw5YpwSi4EUh/i4FooN5I -16OpH5OHwvufNgGQj9LF16Z1LfTrg5Vo7dA0v+1EBFig6jLpByD5SfRcqYtLTwYc -UDQFBR32F7BUd4fUdMQSwQKCAQEA4AWadKLNyXjw7gaqdewyDtPyjhqsth4R+UzU -Cb+Fr6xJv6YLp6sxUwHlynLnTHZWxtBbAM53UHk5PB7d/7EGRbQfPG+i8wjOa3WF -BpcwIRB1M8dKa1StZH1T56H9+cDqr2yXcw0KOdQBZAoIPY/PTFeSQFS4Q8U47XQG -8T5HrQAlIpn96jXDko/4eyhIF6DYo0+RCT47R6UtgPZMVhrh8txN92qhTTYqX/hx -qHJL1I2hXnK4P/7HfAVFPl2I95JY4bcmC/+jEGqZiQEMXWdbQXO/9NdaCcpp1as2 -CpbCe/tA2oNyOGXrH0j9BtYHpVdvcagXi91gxd6+wEa4/285mwKCAQEAxNimA/CI -BJWZN8P8341R0O7002zsrqRdPqRx3NQFkzsS8JSZwvPQCIN8Snao+hkkU5tsglDm -zNZ+RqdQmDTbbeKoiqTO/kWTS96nRu/BQl33Y3JQz4MWDsTNNaRHKvMhnAa11QFT -B1hKH6HxNUF0m0G8nVtQwfRJ+dUGmXKmuxYQXZDFlZw5ofKZ6RC7bldU77Ff6jwp -9OOQmlfxtRFeGH1iweDF3Mob9h0Dfzu6TUxcbbqBloDDawBkTNLvT6yMaBew8CFH -JnILd/tkTZOOCcG3tepfCvERKeFCpzPOHhoRAPZnBhRszjJFJDSfZghRRo3A7XPl -VmXXVTKJCK10ewKCAQEA3GDGxEzQMo2WPiIymJUF3Y5lQ6Q8GWBVgDEzOm+9fMb5 -Od6IAqanfCgWvWTx40dbMHQRwiZaO8E1K86Vx46HRBTg0Zxk6b7VCeNvPL+Iak59 -bbV0oUeI151u6CR067f7Zx1lk5nVYHQN9jLkTmNlo41WY5C0QH8I9Jc6qSICcs78 -uSBSKJBBV7Hn2IgU+6GQ3H9Oh5A/0shMjlw9VktV0Ysl6+pqycEqSITokrP1oyC9 -CWPDm7jw1zF8H9D2B85hP5Lji9Qsvt6PMbblShF+NVJAGQWtHoqQONEX9ay/oCXY -c93xhEfG2Fz+BTaSCPaNvHqGx2G9bIomjpJENYxeVwKCAQBzYstt48DXbxmxJqFE -KYKcBnZcuKzEcnR2E87qsx3Rf/9KJtE7BdAcLXbd71B9yd8RYznn6aRgzhqXL9x+ -W/2EHCjPnGv5gK8m+gzz9/ZBAPPSx1+3RA/Z+GKR3woYkwxQeV58zZnt5EMO586M -eSHxIEd/tddQ2fHDEKwdpqc1Y2mUbxhi6oCd/adahwRXWbngBwlCNKIjeoF497Qn -f1a45EbPfwJ8ubxKOBekrU43oVtMttbfcfsa7c/deIfvHCXxnnGJUPh0AMXYPvQ8 -xRGthnA5oniz4Ts+YVzAxg08d+sftVAOsEpXVABTiMUm+hkqUk2U4yq7yla/CjBp -dcOhAoIBAATy3NTShfK2Cnf6FtbHJaZkrxtQX/O4tilNXSQce9cG+wPqaMasAI1n -vIW5hbBlvawZkbjG2aRwSjivNDXRHjj/HLPjtChtldhf6QiQLtD2sOeDuX1OFJBB -OUEqIZZ1jnPCED/1tzqVQgZMIanAUN/rdB/axm/47QOKY2cOanY/s1KHlUTXaSBI -2TXS9u8vw/WnsW1te4I/vSlQP2WHVYIb1o6lXAcTT3Mq5k+/7zB/zhDFuXL9i2/t -B2JOrecJAdn24mT6LKcc0U73/3gY7+tw2J08gxBu4h1/fVvw613yM2VjfQcctCLP -n8O7s8EqosXOZRYQie6zT0ggrSQam1M= ------END PRIVATE KEY----- diff --git a/samples/IpcServiceSample.ConsoleServer/ComputingService.cs b/samples/IpcServiceSample.ConsoleServer/ComputingService.cs deleted file mode 100644 index 490346f..0000000 --- a/samples/IpcServiceSample.ConsoleServer/ComputingService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using IpcServiceSample.ServiceContracts; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace IpcServiceSample.ConsoleServer -{ - public class ComputingService : IComputingService - { - private readonly ILogger _logger; - - public ComputingService(ILogger logger) // inject dependencies in constructor - { - _logger = logger; - } - - public ComplexNumber AddComplexNumber(ComplexNumber x, ComplexNumber y) - { - _logger.LogInformation($"{nameof(AddComplexNumber)} called."); - return new ComplexNumber(x.A + y.A, x.B + y.B); - } - - public ComplexNumber AddComplexNumbers(IEnumerable numbers) - { - _logger.LogInformation($"{nameof(AddComplexNumbers)} called."); - var result = new ComplexNumber(0, 0); - foreach (ComplexNumber number in numbers) - { - result = new ComplexNumber(result.A + number.A, result.B + number.B); - } - return result; - } - - public float AddFloat(float x, float y) - { - _logger.LogInformation($"{nameof(AddFloat)} called."); - return x + y; - } - - public Task MethodAsync() - { - return Task.CompletedTask; - } - - public Task SumAsync(int x, int y) - { - return Task.FromResult(x + y); - } - } -} diff --git a/samples/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj b/samples/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj deleted file mode 100644 index ebc395d..0000000 --- a/samples/IpcServiceSample.ConsoleServer/IpcServiceSample.ConsoleServer.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - netcoreapp3.1 - - - - - - - - - - - - - - - PreserveNewest - - - - diff --git a/samples/IpcServiceSample.ConsoleServer/Program.cs b/samples/IpcServiceSample.ConsoleServer/Program.cs deleted file mode 100644 index c981087..0000000 --- a/samples/IpcServiceSample.ConsoleServer/Program.cs +++ /dev/null @@ -1,93 +0,0 @@ -using IpcServiceSample.ServiceContracts; -using IpcServiceSample.ServiceContracts.Helpers; -using JKang.IpcServiceFramework; -using JKang.IpcServiceFramework.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; - -namespace IpcServiceSample.ConsoleServer -{ - class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureServices((_, services) => - { - services - .AddScoped() - .AddScoped() - .AddScoped() - ; - }) - .ConfigureIpcHost(builder => - { - builder - .AddNamedPipeEndpoint("endpoint1", "pipeName") - .AddNamedPipeEndpoint("endpoint2", "pipeName2") - ; - }) - .ConfigureLogging(builder => - { - builder - .AddConsole() - .SetMinimumLevel(LogLevel.Debug); - }) - ; - - //static void Main(string[] args) - //{ - // // configure DI - // IServiceCollection services = ConfigureServices(new ServiceCollection()); - - // // build and run service host - // IIpcServiceHost host = new IpcServiceHostBuilder(services.BuildServiceProvider()) - // .AddNamedPipeEndpoint("computingEndpoint", "pipeName") - // .AddTcpEndpoint("systemEndpoint", IPAddress.Loopback, 45684) - // .AddTcpEndpoint("secureEndpoint", IPAddress.Loopback, 44384, new X509Certificate2(@"Certificates\server.pfx", "password")) - // .AddTcpEndpoint("xorTranslatedEndpoint", IPAddress.Loopback, 45454, s => new XorStream(s)) - // .Build(); - - // var source = new CancellationTokenSource(); - // Task.WaitAll(host.RunAsync(source.Token), Task.Run(() => - // { - // Console.WriteLine("Press any key to shutdown."); - // Console.ReadKey(); - // source.Cancel(); - // })); - - // Console.WriteLine("Server stopped."); - //} - - //private static IServiceCollection ConfigureServices(IServiceCollection services) - //{ - // return services - // .AddLogging(builder => - // { - // builder.AddConsole(); - // builder.SetMinimumLevel(LogLevel.Debug); - // }) - // .AddIpc(builder => - // { - // builder - // .AddNamedPipe(options => - // { - // options.ThreadCount = 2; - // }) - // .AddService() - // .AddService() - // .AddService(); - // }); - //} - } -} diff --git a/samples/IpcServiceSample.ConsoleServer/SystemService.cs b/samples/IpcServiceSample.ConsoleServer/SystemService.cs deleted file mode 100644 index e5bb360..0000000 --- a/samples/IpcServiceSample.ConsoleServer/SystemService.cs +++ /dev/null @@ -1,47 +0,0 @@ -using IpcServiceSample.ServiceContracts; -using System; -using System.Globalization; -using System.Linq; -using System.Threading; - -namespace IpcServiceSample.ConsoleServer -{ - public class SystemService : ISystemService - { - public string ConvertText(string text, TextStyle style) - { - switch (style) - { - case TextStyle.TitleCase: - return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(text); - case TextStyle.Upper: - return CultureInfo.InvariantCulture.TextInfo.ToUpper(text); - default: - return text; - } - } - - public void DoNothing() - { } - - public Guid GenerateId() - { - return Guid.NewGuid(); - } - - public string Printout(T value) - { - return value.ToString(); - } - - public byte[] ReverseBytes(byte[] input) - { - return input.Reverse().ToArray(); - } - - public void SlowOperation() - { - Thread.Sleep(10000); - } - } -} diff --git a/samples/IpcServiceSample.ConsoleServer/TestService.cs b/samples/IpcServiceSample.ConsoleServer/TestService.cs deleted file mode 100644 index 448c772..0000000 --- a/samples/IpcServiceSample.ConsoleServer/TestService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using IpcServiceSample.ServiceContracts; -using System; -using System.Globalization; -using System.Linq; -using System.Threading; - -namespace IpcServiceSample.ConsoleServer -{ - public class TestService : ITestService - { - public Guid GenerateId() - { - return Guid.NewGuid(); - } - } -} diff --git a/samples/IpcServiceSample.Server/InterProcessService.cs b/samples/IpcServiceSample.Server/InterProcessService.cs new file mode 100644 index 0000000..e0eae27 --- /dev/null +++ b/samples/IpcServiceSample.Server/InterProcessService.cs @@ -0,0 +1,15 @@ +using IpcServiceSample.ServiceContracts; +using System; + +namespace IpcServiceSample.Server +{ + public class InterProcessService : IInterProcessService + { + public string ReverseString(string input) + { + char[] charArray = input.ToCharArray(); + Array.Reverse(input.ToCharArray()); + return new string(charArray); + } + } +} diff --git a/samples/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj b/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj similarity index 52% rename from samples/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj rename to samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj index b8d0597..1abac5c 100644 --- a/samples/IpcServiceSample.ConsoleClient/IpcServiceSample.ConsoleClient.csproj +++ b/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj @@ -1,13 +1,17 @@ - + Exe netcoreapp3.1 + + + + + - diff --git a/samples/IpcServiceSample.Server/Program.cs b/samples/IpcServiceSample.Server/Program.cs new file mode 100644 index 0000000..7328277 --- /dev/null +++ b/samples/IpcServiceSample.Server/Program.cs @@ -0,0 +1,27 @@ +using IpcServiceSample.Server; +using IpcServiceSample.ServiceContracts; +using JKang.IpcServiceFramework.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace IpcServiceSample.ConsoleServer +{ + class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureServices(services => + { + services.AddScoped(); + }) + .ConfigureIpcHost(builder => + { + builder.AddNamedPipeEndpoint("endpoint1", "pipeinternal"); + }); + } +} diff --git a/samples/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs b/samples/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs deleted file mode 100644 index 01f3159..0000000 --- a/samples/IpcServiceSample.ServiceContracts/Helpers/LoggingStream.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace IpcServiceSample.ServiceContracts.Helpers -{ - public class LoggingStream : Stream - { - private Stream _baseStream; - private StreamWriter _log; - - public LoggingStream(Stream stream, string logFile) - { - _baseStream = stream; - _log = new StreamWriter(logFile) { AutoFlush = true }; - } - - public override bool CanRead => _baseStream.CanRead; - - public override bool CanSeek => _baseStream.CanSeek; - - public override bool CanWrite => _baseStream.CanWrite; - - public override long Length => _baseStream.Length; - - public override long Position { get => _baseStream.Position; set => _baseStream.Position = value; } - - private enum StreamOperation { Read, Write }; - - private void Log(StreamOperation direction, byte[] buffer, int offset, int count) - { - _log.WriteLine($"[{direction.ToString().ToUpper()}: {count}]"); - var dump = new StringBuilder(); - var hex = new StringBuilder(); - var ascii = new StringBuilder(); - for (int i = 0; i < count; i++) - { - if (i > 0 && i % 16 == 0) - { - dump.Append(hex.ToString().PadRight(48, ' ')); - dump.Append(' ', 3); - dump.Append(ascii); - dump.AppendLine(); - hex.Clear(); - ascii.Clear(); - } - - byte c = buffer[offset + i]; - - hex.AppendFormat("{0:x2} ", c); - - if (c >= 32 && c < 255) - ascii.Append((char)c); - else - ascii.Append('.'); - } - if (ascii.Length > 0) - { - dump.Append(hex.ToString().PadRight(48, ' ')); - dump.Append(' ', 3); - dump.Append(ascii); - dump.AppendLine(); - } - - _log.WriteLine(dump.ToString()); - } - - private void Log(string message) - { - _log.WriteLine(message); - } - - public override void Flush() - { - Log("[FLUSH]"); - _baseStream.Flush(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - int br = _baseStream.Read(buffer, offset, count); - Log(StreamOperation.Read, buffer, offset, br); - return br; - } - - public override long Seek(long offset, SeekOrigin origin) - { - Log($"[SEEK: {origin.ToString()}+{offset}]"); - return _baseStream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - Log($"[SET LENGTH: {value}]"); - _baseStream.SetLength(value); - } - - public override void Write(byte[] buffer, int offset, int count) - { - _baseStream.Write(buffer, offset, count); - Log(StreamOperation.Write, buffer, offset, count); - } - } -} diff --git a/samples/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs b/samples/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs deleted file mode 100644 index fe3c8b6..0000000 --- a/samples/IpcServiceSample.ServiceContracts/Helpers/XorStream.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace IpcServiceSample.ServiceContracts.Helpers -{ - public class XorStream : Stream - { - private Stream _baseStream; - - public XorStream(Stream stream) - { - _baseStream = stream; - } - - public override bool CanRead => _baseStream.CanRead; - - public override bool CanSeek => _baseStream.CanSeek; - - public override bool CanWrite => _baseStream.CanWrite; - - public override long Length => _baseStream.Length; - - public override long Position { get => _baseStream.Position; set => _baseStream.Position = value; } - - public override void Flush() - { - _baseStream.Flush(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - int br = _baseStream.Read(buffer, offset, count); - for (int i = offset; i < offset + br; i++) - buffer[i] ^= 0xFF; - return br; - } - - public override long Seek(long offset, SeekOrigin origin) - { - return _baseStream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - _baseStream.SetLength(value); - } - - public override void Write(byte[] buffer, int offset, int count) - { - byte[] xoredBuffer = new byte[count]; - for (int i = 0; i < count; i++) - xoredBuffer[i] = (byte)(buffer[offset + i] ^ 0xFF); - _baseStream.Write(xoredBuffer, 0, count); - } - } -} diff --git a/samples/IpcServiceSample.ServiceContracts/IComputingService.cs b/samples/IpcServiceSample.ServiceContracts/IComputingService.cs deleted file mode 100644 index d032d5d..0000000 --- a/samples/IpcServiceSample.ServiceContracts/IComputingService.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace IpcServiceSample.ServiceContracts -{ - public interface IComputingService - { - float AddFloat(float x, float y); - ComplexNumber AddComplexNumber(ComplexNumber x, ComplexNumber y); - ComplexNumber AddComplexNumbers(IEnumerable numbers); - - Task MethodAsync(); - Task SumAsync(int x, int y); - } - - public class ComplexNumber - { - public float A { get; set; } - public float B { get; set; } - - public ComplexNumber(float a, float b) - { - A = a; - B = b; - } - } - - public enum TextStyle - { - TitleCase, - Upper - } -} diff --git a/samples/IpcServiceSample.ServiceContracts/IInterProcessService.cs b/samples/IpcServiceSample.ServiceContracts/IInterProcessService.cs new file mode 100644 index 0000000..8b8733b --- /dev/null +++ b/samples/IpcServiceSample.ServiceContracts/IInterProcessService.cs @@ -0,0 +1,7 @@ +namespace IpcServiceSample.ServiceContracts +{ + public interface IInterProcessService + { + string ReverseString(string input); + } +} diff --git a/samples/IpcServiceSample.ServiceContracts/ISystemService.cs b/samples/IpcServiceSample.ServiceContracts/ISystemService.cs deleted file mode 100644 index 64165e9..0000000 --- a/samples/IpcServiceSample.ServiceContracts/ISystemService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace IpcServiceSample.ServiceContracts -{ - public interface ISystemService - { - void DoNothing(); - string ConvertText(string text, TextStyle style); - Guid GenerateId(); - byte[] ReverseBytes(byte[] input); - string Printout(T value); - void SlowOperation(); - } -} diff --git a/samples/IpcServiceSample.ServiceContracts/ITestService.cs b/samples/IpcServiceSample.ServiceContracts/ITestService.cs deleted file mode 100644 index 9e936f2..0000000 --- a/samples/IpcServiceSample.ServiceContracts/ITestService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace IpcServiceSample.ServiceContracts -{ - public interface ITestService - { - Guid GenerateId(); - } -} diff --git a/samples/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj b/samples/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj index bbd5440..9f5c4f4 100644 --- a/samples/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj +++ b/samples/IpcServiceSample.ServiceContracts/IpcServiceSample.ServiceContracts.csproj @@ -2,7 +2,6 @@ netstandard2.0 - IpcServiceSample.ServiceContracts diff --git a/samples/IpcServiceSample.WebServer/Certificates/cert.crt b/samples/IpcServiceSample.WebServer/Certificates/cert.crt deleted file mode 100644 index 426a5c9..0000000 --- a/samples/IpcServiceSample.WebServer/Certificates/cert.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFfTCCA2WgAwIBAgIUCTca4HpnnD5ZL3E3YIiM+meBmLYwDQYJKoZIhvcNAQEL -BQAwTjELMAkGA1UEBhMCWFgxDTALBgNVBAgMBFRlc3QxDTALBgNVBAoMBFRlc3Qx -ITAfBgNVBAMMGHRlc3QtaXBjc2Ytc2VjdXJlLXNlcnZlcjAeFw0xODEyMTIyMjQ4 -NDNaFw0yODEyMDkyMjQ4NDNaME4xCzAJBgNVBAYTAlhYMQ0wCwYDVQQIDARUZXN0 -MQ0wCwYDVQQKDARUZXN0MSEwHwYDVQQDDBh0ZXN0LWlwY3NmLXNlY3VyZS1zZXJ2 -ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsQeBCs8OknFXnlz0i -a6ScSF2cS/zKKpExDfhcLlGRhcXwydu1q3SMumvFgOMqKeBs7oM2JT1VqEWC77D6 -Cct+rIAdEw/Km1njYxLjo4buYIwJbNeJT+iZv0sSFJvx+Go15n6QfrFcmVGy52Hs -BkD7SQTSLv9D5UwysNn7i/BcSXZPw272oobYqRKeQQ0aVMCP7f0O1vXcJVVsER0H -YpI0m7BTpIFQbGg8jnjSue0AKQmYyzoroOwW1B7y/lp8W43lCL40ZMiBwZQ6jFFp -OHBB+SxQqzWr6duBxA6F96tlDEXZXCJoOt0nIVXtXcv5q5xpcuiIil21aIVMSw3o -Eo7ccvIm/gTCUnkJ0ahx7DAX1jaTt5Zh8K78z0CXxr5pg+Q/BFdGZ0iaYBtKX+yL -1vcWLusKYhI2IEFgtJXhfjaYm/rhTjS4SCzmAMexWPWJXy/Bu1nh2fSrMMZeWyeu -S/0T60cZav/Pqqat+ReufvxRlpYlq8KfOuVrfbwbYZwR4NyVRDPNhAMgTZngqTbN -5xsqrH1pNspwU8emJOUvH0CWY4/qRr5uJzbNsBS9nbJERAQe0wsPpw8ZOgItmEX/ -X562cjGrCXvaQewh2a17kvFq+eEpR/pMs/JBGT22wFVfof0Sy/Fl0w0hHW4QPM7e -+I9mbNuedK4w6vUSqwCK5knpeQIDAQABo1MwUTAdBgNVHQ4EFgQU3Xxk9XFDaGnZ -zck+or/hA5yRJEswHwYDVR0jBBgwFoAU3Xxk9XFDaGnZzck+or/hA5yRJEswDwYD -VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAVih/7NFQsbjieZZcxqvk -XhfnMMXtJZiHnTS5gFEtbAwuhCYE2yrhXngGKXSQX2XCcuSzvgVysiV/prN+KVHf -APZXiGjOaCJwPHkiCeJ/bub+cLclnm3dNnoHgmKhIZgv7C/bAnGGdcwlOP76lgg2 -c/pEuFyfN1mwEiHjO/ryEHF3KxpGvdlkHZXnSoRP+Ghk0PCmwJkyRaCkn6YbeAq2 -+7CVnlMwzCqsIRb2yz/0COWJYTH3w39Ejf5HGGl2TKQ3mt1425m5j/kJdBxCE6cC -epufzBae143qvkDGN4/3Xhb3QGxh7ep9RGXTgPHlQqXMS6gWpa9dJ1wGJb1M4O92 -V6c+Gp87mA42/sew1kBERx8KupgM/zTatRO+b5DCRkTuZ/aQvxaod3c++0WeufsC -IFMOGkqYNDot2bAqFQKqg72FXR0OD/Iq4R6ZQNeYUoNH3c4ev3zcevPzaO9wwP06 -C5vfp4QivK/3vcjHMxfCEPm3KNls/U6syTTH12RHguGUHsEd7SY4S7n3oJoziXtf -yzKQF+CkDTWC86pjm9bN3AchObYJIbVx+vZnWOzDmOZTE0AshGWxlWTR3TMJLiNr -sgtTfiNEN+6HbKh4aoDAtapwyoGy0LfqoiLHAYjp8ePS1v0Lr80c5yoPDLrfdYHK -ZHQzl1Ey1cFJr0zpx9Bi33I= ------END CERTIFICATE----- diff --git a/samples/IpcServiceSample.WebServer/Certificates/key.pem b/samples/IpcServiceSample.WebServer/Certificates/key.pem deleted file mode 100644 index 1d5c7ec..0000000 --- a/samples/IpcServiceSample.WebServer/Certificates/key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCsQeBCs8OknFXn -lz0ia6ScSF2cS/zKKpExDfhcLlGRhcXwydu1q3SMumvFgOMqKeBs7oM2JT1VqEWC -77D6Cct+rIAdEw/Km1njYxLjo4buYIwJbNeJT+iZv0sSFJvx+Go15n6QfrFcmVGy -52HsBkD7SQTSLv9D5UwysNn7i/BcSXZPw272oobYqRKeQQ0aVMCP7f0O1vXcJVVs -ER0HYpI0m7BTpIFQbGg8jnjSue0AKQmYyzoroOwW1B7y/lp8W43lCL40ZMiBwZQ6 -jFFpOHBB+SxQqzWr6duBxA6F96tlDEXZXCJoOt0nIVXtXcv5q5xpcuiIil21aIVM -Sw3oEo7ccvIm/gTCUnkJ0ahx7DAX1jaTt5Zh8K78z0CXxr5pg+Q/BFdGZ0iaYBtK -X+yL1vcWLusKYhI2IEFgtJXhfjaYm/rhTjS4SCzmAMexWPWJXy/Bu1nh2fSrMMZe -WyeuS/0T60cZav/Pqqat+ReufvxRlpYlq8KfOuVrfbwbYZwR4NyVRDPNhAMgTZng -qTbN5xsqrH1pNspwU8emJOUvH0CWY4/qRr5uJzbNsBS9nbJERAQe0wsPpw8ZOgIt -mEX/X562cjGrCXvaQewh2a17kvFq+eEpR/pMs/JBGT22wFVfof0Sy/Fl0w0hHW4Q -PM7e+I9mbNuedK4w6vUSqwCK5knpeQIDAQABAoICAQCUKFTrChLMEmsQtlQutsbu -ZI+fTvwuJk6bEpj7MBuYPqbxY61FpCKqp+zqAuFf8oTFLKBOgdkvQ3wGEoL1jFcq -rNPELhD3AoddvGkSwiPcA85ujN8Vi1VUZ+P5uSAoDrHLimRxg4apTnWmmrzudLKP -b05mOWX0z9OqBdJ3OPWTatwH3uAh4ch5sXICC5FphFvbb6aojNsKblH6kP2WzIFU -HlSanHNc6OD+tMvW83OVH7bRZHqz68UkHW5BMeRB8b0psUtnZQfQEt+bO/UJuzFS -jS5AdAHFy26xPh//ufYBA31QZp5xZ6+vaEyvzG0UYTY2vE6kod3Xmf6MkEF1ygB0 -0arsKvpTi9DN3tT02oxO1zrGj35Hn5Hmdq4SjexRHTRktlOpY6e7pOYK1fnKxXlX -OVn7+iaFBIM2M1GhXdcuetMd4cswtKbOwR5K4HpO3op64IeiNlh2OdrzkbW+67s0 -g5N3kNZ6wZ4x8Syim8SsEEYHu1bhOvp5Im3zCVdVOxvkYogjxVc+COayu0ROPR4K -81DLX5oI8AV7LkRVNiAH4/c/qRm2eTPdVtrrSBZ+GoSBw5YpwSi4EUh/i4FooN5I -16OpH5OHwvufNgGQj9LF16Z1LfTrg5Vo7dA0v+1EBFig6jLpByD5SfRcqYtLTwYc -UDQFBR32F7BUd4fUdMQSwQKCAQEA4AWadKLNyXjw7gaqdewyDtPyjhqsth4R+UzU -Cb+Fr6xJv6YLp6sxUwHlynLnTHZWxtBbAM53UHk5PB7d/7EGRbQfPG+i8wjOa3WF -BpcwIRB1M8dKa1StZH1T56H9+cDqr2yXcw0KOdQBZAoIPY/PTFeSQFS4Q8U47XQG -8T5HrQAlIpn96jXDko/4eyhIF6DYo0+RCT47R6UtgPZMVhrh8txN92qhTTYqX/hx -qHJL1I2hXnK4P/7HfAVFPl2I95JY4bcmC/+jEGqZiQEMXWdbQXO/9NdaCcpp1as2 -CpbCe/tA2oNyOGXrH0j9BtYHpVdvcagXi91gxd6+wEa4/285mwKCAQEAxNimA/CI -BJWZN8P8341R0O7002zsrqRdPqRx3NQFkzsS8JSZwvPQCIN8Snao+hkkU5tsglDm -zNZ+RqdQmDTbbeKoiqTO/kWTS96nRu/BQl33Y3JQz4MWDsTNNaRHKvMhnAa11QFT -B1hKH6HxNUF0m0G8nVtQwfRJ+dUGmXKmuxYQXZDFlZw5ofKZ6RC7bldU77Ff6jwp -9OOQmlfxtRFeGH1iweDF3Mob9h0Dfzu6TUxcbbqBloDDawBkTNLvT6yMaBew8CFH -JnILd/tkTZOOCcG3tepfCvERKeFCpzPOHhoRAPZnBhRszjJFJDSfZghRRo3A7XPl -VmXXVTKJCK10ewKCAQEA3GDGxEzQMo2WPiIymJUF3Y5lQ6Q8GWBVgDEzOm+9fMb5 -Od6IAqanfCgWvWTx40dbMHQRwiZaO8E1K86Vx46HRBTg0Zxk6b7VCeNvPL+Iak59 -bbV0oUeI151u6CR067f7Zx1lk5nVYHQN9jLkTmNlo41WY5C0QH8I9Jc6qSICcs78 -uSBSKJBBV7Hn2IgU+6GQ3H9Oh5A/0shMjlw9VktV0Ysl6+pqycEqSITokrP1oyC9 -CWPDm7jw1zF8H9D2B85hP5Lji9Qsvt6PMbblShF+NVJAGQWtHoqQONEX9ay/oCXY -c93xhEfG2Fz+BTaSCPaNvHqGx2G9bIomjpJENYxeVwKCAQBzYstt48DXbxmxJqFE -KYKcBnZcuKzEcnR2E87qsx3Rf/9KJtE7BdAcLXbd71B9yd8RYznn6aRgzhqXL9x+ -W/2EHCjPnGv5gK8m+gzz9/ZBAPPSx1+3RA/Z+GKR3woYkwxQeV58zZnt5EMO586M -eSHxIEd/tddQ2fHDEKwdpqc1Y2mUbxhi6oCd/adahwRXWbngBwlCNKIjeoF497Qn -f1a45EbPfwJ8ubxKOBekrU43oVtMttbfcfsa7c/deIfvHCXxnnGJUPh0AMXYPvQ8 -xRGthnA5oniz4Ts+YVzAxg08d+sftVAOsEpXVABTiMUm+hkqUk2U4yq7yla/CjBp -dcOhAoIBAATy3NTShfK2Cnf6FtbHJaZkrxtQX/O4tilNXSQce9cG+wPqaMasAI1n -vIW5hbBlvawZkbjG2aRwSjivNDXRHjj/HLPjtChtldhf6QiQLtD2sOeDuX1OFJBB -OUEqIZZ1jnPCED/1tzqVQgZMIanAUN/rdB/axm/47QOKY2cOanY/s1KHlUTXaSBI -2TXS9u8vw/WnsW1te4I/vSlQP2WHVYIb1o6lXAcTT3Mq5k+/7zB/zhDFuXL9i2/t -B2JOrecJAdn24mT6LKcc0U73/3gY7+tw2J08gxBu4h1/fVvw613yM2VjfQcctCLP -n8O7s8EqosXOZRYQie6zT0ggrSQam1M= ------END PRIVATE KEY----- diff --git a/samples/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj b/samples/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj deleted file mode 100644 index 2eec263..0000000 --- a/samples/IpcServiceSample.WebServer/IpcServiceSample.WebServer.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - netcoreapp3.1 - - - - - - - - - - - - - - - PreserveNewest - - - - diff --git a/samples/IpcServiceSample.WebServer/Program.cs b/samples/IpcServiceSample.WebServer/Program.cs deleted file mode 100644 index 1143137..0000000 --- a/samples/IpcServiceSample.WebServer/Program.cs +++ /dev/null @@ -1,41 +0,0 @@ -using IpcServiceSample.ServiceContracts; -using JKang.IpcServiceFramework; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using System.Threading; - -namespace IpcServiceSample.WebServer -{ - public class Program - { - public static void Main(string[] args) - { - IWebHost webHost = BuildWebHost(args); - - ThreadPool.QueueUserWorkItem(StartIpcService, - webHost.Services.CreateScope().ServiceProvider); - - webHost.Run(); - } - - private static void StartIpcService(object state) - { - var serviceProvider = state as IServiceProvider; - new IpcServiceHostBuilder(serviceProvider) - .AddNamedPipeEndpoint("computingEndpoint", "pipeName") - .AddTcpEndpoint("systemEndpoint", IPAddress.Loopback, 45684) - .AddTcpEndpoint("secureEndpoint", IPAddress.Loopback, 44384, new X509Certificate2(@"Certificates\server.pfx", "password")) - .Build() - .Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup() - .Build(); - } -} diff --git a/samples/IpcServiceSample.WebServer/Startup.cs b/samples/IpcServiceSample.WebServer/Startup.cs deleted file mode 100644 index cdd046c..0000000 --- a/samples/IpcServiceSample.WebServer/Startup.cs +++ /dev/null @@ -1,40 +0,0 @@ -using IpcServiceSample.ConsoleServer; -using IpcServiceSample.ServiceContracts; -using JKang.IpcServiceFramework; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace IpcServiceSample.WebServer -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services - .AddIpc(builder => - { - builder - .AddNamedPipe() - .AddService() - .AddService() - .AddService(); - }); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.Run(async (context) => - { - await context.Response.WriteAsync("Hello World!"); - }); - } - } -} diff --git a/samples/IpcServiceSample.sln b/samples/IpcServiceSample.sln new file mode 100644 index 0000000..70784c8 --- /dev/null +++ b/samples/IpcServiceSample.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.ServiceContracts", "IpcServiceSample.ServiceContracts\IpcServiceSample.ServiceContracts.csproj", "{75CA748C-4346-4266-B729-ACC4124B5C15}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.ConsoleClient", "IpcServiceSample.Client\IpcServiceSample.ConsoleClient.csproj", "{A3F3B3FA-4374-4DDB-B815-427C03B06F69}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.Server", "IpcServiceSample.Server\IpcServiceSample.Server.csproj", "{39CFB883-D3E3-43D0-A94C-4F587C45294B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {75CA748C-4346-4266-B729-ACC4124B5C15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75CA748C-4346-4266-B729-ACC4124B5C15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75CA748C-4346-4266-B729-ACC4124B5C15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75CA748C-4346-4266-B729-ACC4124B5C15}.Release|Any CPU.Build.0 = Release|Any CPU + {A3F3B3FA-4374-4DDB-B815-427C03B06F69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3F3B3FA-4374-4DDB-B815-427C03B06F69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3F3B3FA-4374-4DDB-B815-427C03B06F69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3F3B3FA-4374-4DDB-B815-427C03B06F69}.Release|Any CPU.Build.0 = Release|Any CPU + {39CFB883-D3E3-43D0-A94C-4F587C45294B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39CFB883-D3E3-43D0-A94C-4F587C45294B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39CFB883-D3E3-43D0-A94C-4F587C45294B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39CFB883-D3E3-43D0-A94C-4F587C45294B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F174C67D-CEF7-4016-8DB6-94ED131FF8E6} + EndGlobalSection +EndGlobal diff --git a/samples/Samples/Samples.sln b/samples/Samples/Samples.sln deleted file mode 100644 index e70cf5a..0000000 --- a/samples/Samples/Samples.sln +++ /dev/null @@ -1,13 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30011.22 -MinimumVisualStudioVersion = 10.0.40219.1 -Global - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {F174C67D-CEF7-4016-8DB6-94ED131FF8E6} - EndGlobalSection -EndGlobal diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs index 3347f8a..6929442 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs @@ -4,7 +4,7 @@ namespace JKang.IpcServiceFramework { public class IpcRequest { - private Type[] _genericArguments = new Type[0]; + private Type[] _genericArguments = Array.Empty(); public string MethodName { get; set; } public object[] Parameters { get; set; } @@ -12,11 +12,11 @@ public class IpcRequest /// /// Gets or sets the types of parameter of the IPC method to call /// - public Type[] ParameterTypes { get; set; } = new Type[0]; + public Type[] ParameterTypes { get; set; } = Array.Empty(); public Type[] GenericArguments { - get => _genericArguments ?? new Type[0]; + get => _genericArguments ?? Array.Empty(); set => _genericArguments = value; } } diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs index c804f3d..3776602 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs @@ -1,14 +1,13 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; namespace JKang.IpcServiceFramework.Hosting { - public interface IIpcEndpoint + public interface IIpcEndpoint: IDisposable { string Name { get; } - Task StartAsync(CancellationToken cancellationToken = default); - - Task StopAsync(CancellationToken cancellationToken = default); + Task ExecuteAsync(CancellationToken stoppingToken); } } diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs index 97fa8dc..0151810 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs @@ -1,6 +1,7 @@ using JKang.IpcServiceFramework.Services; using Microsoft.Extensions.Logging; using System; +using System.IO; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; @@ -24,13 +25,20 @@ public NamedPipeIpcServiceEndpoint( _options = options; } - protected override async Task WaitAndProcessAsync(CancellationToken cancellationToken) + protected override async Task WaitAndProcessAsync( + Func process, + CancellationToken cancellationToken) { + if (process is null) + { + throw new ArgumentNullException(nameof(process)); + } + using (var server = new NamedPipeServerStream(_options.PipeName, PipeDirection.InOut, _options.MaxConcurrentCalls, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) { await server.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false); - await ProcessAsync(server, cancellationToken).ConfigureAwait(false); + await process(server, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs index e92a678..6a9f66d 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs @@ -29,8 +29,15 @@ public TcpIpcEndpoint( _listener.Start(); } - protected override async Task WaitAndProcessAsync(CancellationToken cancellationToken) + protected override async Task WaitAndProcessAsync( + Func process, + CancellationToken cancellationToken) { + if (process is null) + { + throw new ArgumentNullException(nameof(process)); + } + using (TcpClient client = await _listener.AcceptTcpClientAsync().ConfigureAwait(false)) { Stream server = client.GetStream(); @@ -39,13 +46,18 @@ protected override async Task WaitAndProcessAsync(CancellationToken cancellation // if SSL is enabled, wrap the stream in an SslStream in client mode if (_options.EnableSsl) { - var ssl = new SslStream(server, false); - ssl.AuthenticateAsServer(_options.SslCertificate - ?? throw new IpcHostingConfigurationException("Invalid TCP IPC endpoint configured: SSL enabled without providing certificate.")); - server = ssl; + using (var ssl = new SslStream(server, false)) + { + ssl.AuthenticateAsServer(_options.SslCertificate + ?? throw new IpcHostingConfigurationException("Invalid TCP IPC endpoint configured: SSL enabled without providing certificate.")); + await process(ssl, cancellationToken).ConfigureAwait(false); + } + } + else + { + await process(server, cancellationToken).ConfigureAwait(false); } - await ProcessAsync(server, cancellationToken).ConfigureAwait(false); client.Close(); } } diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs index bee73eb..35f6131 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs @@ -15,12 +15,12 @@ namespace JKang.IpcServiceFramework.Hosting public abstract class IpcEndpoint : IIpcEndpoint where TContract : class { - private readonly CancellationTokenSource _cts = new CancellationTokenSource(); private readonly IpcEndpointOptions _options; private readonly IServiceProvider _serviceProvider; private readonly IIpcMessageSerializer _serializer; private readonly IValueConverter _valueConverter; private readonly ILogger _logger; + private readonly SemaphoreSlim _semaphore; protected IpcEndpoint( string name, @@ -31,18 +31,39 @@ protected IpcEndpoint( ILogger logger) { Name = name; - _options = options; - _serviceProvider = serviceProvider; - _serializer = serializer; - _valueConverter = valueConverter; - _logger = logger; + _options = options ?? throw new ArgumentNullException(nameof(options)); + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + _valueConverter = valueConverter ?? throw new ArgumentNullException(nameof(valueConverter)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _semaphore = new SemaphoreSlim(options.MaxConcurrentCalls); } public string Name { get; } + public async Task ExecuteAsync(CancellationToken stoppingToken) + { + var factory = new TaskFactory(TaskScheduler.Default); + while (!stoppingToken.IsCancellationRequested) + { + await _semaphore.WaitAsync(stoppingToken).ConfigureAwait(false); + + WaitAndProcessAsync(ProcessAsync, stoppingToken).ContinueWith(task => + { + if (task.IsFaulted) + { + _logger.LogError(task.Exception, "Error occurred"); + } + return _semaphore.Release(); + }); + } + } + + protected abstract Task WaitAndProcessAsync(Func process, CancellationToken stoppingToken); + protected virtual Stream TransformStream(Stream input) => input; - protected async Task ProcessAsync(Stream server, CancellationToken cancellationToken) + private async Task ProcessAsync(Stream server, CancellationToken stoppingToken) { server = TransformStream(server); using (var writer = new IpcWriter(server, _serializer, leaveOpen: true)) @@ -50,15 +71,15 @@ protected async Task ProcessAsync(Stream server, CancellationToken cancellationT { try { - if (cancellationToken.IsCancellationRequested) + if (stoppingToken.IsCancellationRequested) { return; } _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] client connected, reading request..."); - IpcRequest request = await reader.ReadIpcRequestAsync(cancellationToken).ConfigureAwait(false); + IpcRequest request = await reader.ReadIpcRequestAsync(stoppingToken).ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); + stoppingToken.ThrowIfCancellationRequested(); _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] request received, invoking '{request.MethodName}'..."); IpcResponse response; @@ -67,10 +88,10 @@ protected async Task ProcessAsync(Stream server, CancellationToken cancellationT response = await GetReponse(request, scope).ConfigureAwait(false); } - cancellationToken.ThrowIfCancellationRequested(); + stoppingToken.ThrowIfCancellationRequested(); _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] sending response..."); - await writer.WriteAsync(response, cancellationToken).ConfigureAwait(false); + await writer.WriteAsync(response, stoppingToken).ConfigureAwait(false); _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] done."); } @@ -78,12 +99,12 @@ protected async Task ProcessAsync(Stream server, CancellationToken cancellationT { var response = IpcResponse.Fail(ex, _options.IncludeFailureDetailsInResponse); _logger.LogError(ex, response.Failure); - await writer.WriteAsync(response, cancellationToken).ConfigureAwait(false); + await writer.WriteAsync(response, stoppingToken).ConfigureAwait(false); } } } - protected async Task GetReponse(IpcRequest request, IServiceScope scope) + private async Task GetReponse(IpcRequest request, IServiceScope scope) { if (request is null) { @@ -232,42 +253,31 @@ public static MethodInfo GetUnambiguousMethod(IpcRequest request, object service return method; } - public virtual Task StartAsync(CancellationToken cancellationToken = default) - { - Task.Run(() => - { - var semaphore = new SemaphoreSlim(_options.MaxConcurrentCalls); - while (!_cts.IsCancellationRequested) - { - semaphore.Wait(); - WaitAndProcessAsync(_cts.Token) - .ContinueWith(task => - { - if (task.IsFaulted) - { - _logger.LogError(task.Exception, "Error occurred"); - } - return semaphore.Release(); - }); - } - }); + #region IDisposable - _logger.LogInformation("IPC endpoint '{EndpointName}' started.", Name); - return Task.CompletedTask; - } + private bool _disposed; - public virtual async Task StopAsync(CancellationToken cancellationToken = default) + protected virtual void Dispose(bool disposing) { - _cts.Cancel(); + if (_disposed) + { + return; + } - await Task.Run(() => + if (disposing) { - WaitHandle.WaitAny(new[] { _cts.Token.WaitHandle }); - }, cancellationToken).ConfigureAwait(false); + _semaphore.Dispose(); + } - _logger.LogInformation("IPC endpoint '{EndpointName}' stopped.", Name); + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); } - protected abstract Task WaitAndProcessAsync(CancellationToken cancellationToken); + #endregion } } diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs b/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs index 88e726a..0215d79 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs @@ -6,7 +6,7 @@ namespace JKang.IpcServiceFramework.Hosting { - public class IpcHostedService : IHostedService + public sealed class IpcHostedService : BackgroundService { private readonly IEnumerable _endpoints; private readonly ILogger _logger; @@ -15,26 +15,27 @@ public IpcHostedService( IEnumerable endpoints, ILogger logger) { - _endpoints = endpoints; - _logger = logger; + _endpoints = endpoints ?? throw new System.ArgumentNullException(nameof(endpoints)); + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); } - public async Task StartAsync(CancellationToken cancellationToken) + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { foreach (IIpcEndpoint endpoint in _endpoints) { - await endpoint.StartAsync(cancellationToken).ConfigureAwait(false); + await endpoint.ExecuteAsync(stoppingToken).ConfigureAwait(false); + _logger.LogDebug("Started endpoint {EndpointName}.", endpoint.Name); } - _logger.LogInformation("IPC hosted service started."); } - public async Task StopAsync(CancellationToken cancellationToken) + public override void Dispose() { foreach (IIpcEndpoint endpoint in _endpoints) { - await endpoint.StopAsync(cancellationToken).ConfigureAwait(false); + endpoint.Dispose(); } - _logger.LogInformation("IPC hosted service stopped."); + + base.Dispose(); } } } From 4819c7564527c22d9ff7cf8ca6a1adb9dc0678a4 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Mon, 27 Apr 2020 13:40:32 +0200 Subject: [PATCH 21/53] Refactor/sample improvement (#100) * refactor: update sample project * refactor: simplify IpcHostedService by inheriting from BackgroundService * refactor: fix failing build * refactor: improve sample Co-authored-by: Jacques Kang --- .../IpcServiceSample.ConsoleClient.csproj | 2 +- samples/IpcServiceSample.Client/Program.cs | 6 ++++-- samples/IpcServiceSample.Server/InterProcessService.cs | 2 +- .../IpcServiceSample.Server/IpcServiceSample.Server.csproj | 2 +- samples/IpcServiceSample.Server/Program.cs | 5 +++++ 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj b/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj index 02e4e38..8a890c3 100644 --- a/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj +++ b/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/IpcServiceSample.Client/Program.cs b/samples/IpcServiceSample.Client/Program.cs index b590bbd..9d8ab3f 100644 --- a/samples/IpcServiceSample.Client/Program.cs +++ b/samples/IpcServiceSample.Client/Program.cs @@ -13,13 +13,15 @@ private static async Task Main(string[] args) Console.WriteLine("Type a phrase and press enter:"); string input = Console.ReadLine(); + Console.WriteLine("Invoking inter-process service..."); IIpcClient client = new ServiceCollection() .AddNamedPipeIpcClient("pipeinternal") .BuildServiceProvider() .GetRequiredService>(); - string output = await client.InvokeAsync(x => x.ReverseString(input)); - Console.WriteLine(output); + + Console.WriteLine($"Result from server: '{output}'"); + Console.WriteLine("Press any key to exit."); } } } diff --git a/samples/IpcServiceSample.Server/InterProcessService.cs b/samples/IpcServiceSample.Server/InterProcessService.cs index e0eae27..51a887a 100644 --- a/samples/IpcServiceSample.Server/InterProcessService.cs +++ b/samples/IpcServiceSample.Server/InterProcessService.cs @@ -8,7 +8,7 @@ public class InterProcessService : IInterProcessService public string ReverseString(string input) { char[] charArray = input.ToCharArray(); - Array.Reverse(input.ToCharArray()); + Array.Reverse(charArray); return new string(charArray); } } diff --git a/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj b/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj index 1abac5c..eab2854 100644 --- a/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj +++ b/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/IpcServiceSample.Server/Program.cs b/samples/IpcServiceSample.Server/Program.cs index 7328277..c7c3670 100644 --- a/samples/IpcServiceSample.Server/Program.cs +++ b/samples/IpcServiceSample.Server/Program.cs @@ -3,6 +3,7 @@ using JKang.IpcServiceFramework.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace IpcServiceSample.ConsoleServer { @@ -22,6 +23,10 @@ public static IHostBuilder CreateHostBuilder(string[] args) => .ConfigureIpcHost(builder => { builder.AddNamedPipeEndpoint("endpoint1", "pipeinternal"); + }) + .ConfigureLogging(builder => + { + builder.SetMinimumLevel(LogLevel.Information); }); } } From 9fbf1f4a62c604c23906365b076faa22bf848d15 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Mon, 27 Apr 2020 14:56:49 +0200 Subject: [PATCH 22/53] refactor: fix fxcop warnings (#101) Co-authored-by: Jacques Kang --- .../IpcRequest.cs | 2 + .../IpcResponse.cs | 5 ++ .../IpcServerException.cs | 6 ++- .../IpcServerUserCodeException.cs | 5 ++ .../Services/DefaultValueConverter.cs | 22 +++++++-- ...stedService.cs => IpcBackgroundService.cs} | 13 +++-- .../IpcEndpoint.cs | 47 ++++++++++--------- .../IpcHostBuilder.cs | 2 +- 8 files changed, 66 insertions(+), 36 deletions(-) rename src/JKang.IpcServiceFramework.Hosting/{IpcHostedService.cs => IpcBackgroundService.cs} (65%) diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs index 6929442..52a2c73 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs @@ -2,6 +2,7 @@ namespace JKang.IpcServiceFramework { +#pragma warning disable CA1819 // Properties should not return arrays public class IpcRequest { private Type[] _genericArguments = Array.Empty(); @@ -20,4 +21,5 @@ public Type[] GenericArguments set => _genericArguments = value; } } +#pragma warning restore CA1819 // Properties should not return arrays } diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs index a60c4ff..560293c 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs @@ -21,6 +21,11 @@ public static IpcResponse Fail(string failure) public static IpcResponse Fail(Exception ex, bool includeDetails, bool userFailure = false) { + if (ex is null) + { + throw new ArgumentNullException(nameof(ex)); + } + string message = null; string details = null; diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs index c786e48..0c4864a 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs @@ -14,8 +14,10 @@ public class IpcServerException : InvalidOperationException public string FailureDetails { get; } public IpcServerException() - { - } + { } + + public IpcServerException(string message) : base(message) + { } public IpcServerException(string message, string failureDetails) : base(message) diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs index 98ec87a..acf169a 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs @@ -13,6 +13,11 @@ public IpcServerUserCodeException() { } + public IpcServerUserCodeException(string message) + : base(message) + { + } + public IpcServerUserCodeException(string message, string failureDetails) : base(message, failureDetails) { diff --git a/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs b/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs index ab9b4bc..20febea 100644 --- a/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs +++ b/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; +using System.Globalization; namespace JKang.IpcServiceFramework.Services { @@ -35,7 +36,11 @@ public bool TryConvert(object origValue, Type destType, out object destValue) destValue = Enum.Parse(destType, str, ignoreCase: true); return true; } - catch + catch (Exception ex) when ( + ex is ArgumentNullException || + ex is ArgumentException || + ex is OverflowException + ) { } } else @@ -45,7 +50,10 @@ public bool TryConvert(object origValue, Type destType, out object destValue) destValue = Enum.ToObject(destType, origValue); return true; } - catch + catch (Exception ex) when ( + ex is ArgumentNullException || + ex is ArgumentException + ) { } } } @@ -75,10 +83,14 @@ public bool TryConvert(object origValue, Type destType, out object destValue) try { - destValue = Convert.ChangeType(origValue, destType); + destValue = Convert.ChangeType(origValue, destType, CultureInfo.InvariantCulture); return true; } - catch + catch (Exception ex) when ( + ex is InvalidCastException || + ex is FormatException || + ex is OverflowException || + ex is ArgumentNullException) { } try @@ -86,7 +98,7 @@ public bool TryConvert(object origValue, Type destType, out object destValue) destValue = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(origValue), destType); return true; } - catch + catch (JsonException) { } destValue = null; diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs b/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs similarity index 65% rename from src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs rename to src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs index 0215d79..6b6ac93 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcHostedService.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs @@ -6,14 +6,14 @@ namespace JKang.IpcServiceFramework.Hosting { - public sealed class IpcHostedService : BackgroundService + public sealed class IpcBackgroundService : BackgroundService { private readonly IEnumerable _endpoints; - private readonly ILogger _logger; + private readonly ILogger _logger; - public IpcHostedService( + public IpcBackgroundService( IEnumerable endpoints, - ILogger logger) + ILogger logger) { _endpoints = endpoints ?? throw new System.ArgumentNullException(nameof(endpoints)); _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); @@ -24,8 +24,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) foreach (IIpcEndpoint endpoint in _endpoints) { await endpoint.ExecuteAsync(stoppingToken).ConfigureAwait(false); - _logger.LogDebug("Started endpoint {EndpointName}.", endpoint.Name); + _logger.LogDebug("Endpoint '{EndpointName}' started.", endpoint.Name); } + _logger.LogInformation("IPC background service started."); } public override void Dispose() @@ -33,9 +34,11 @@ public override void Dispose() foreach (IIpcEndpoint endpoint in _endpoints) { endpoint.Dispose(); + _logger.LogDebug("Endpoint '{EndpointName}' disposed.", endpoint.Name); } base.Dispose(); + _logger.LogInformation("IPC background service disposed."); } } } diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs index 35f6131..aa27e29 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -48,6 +49,7 @@ public async Task ExecuteAsync(CancellationToken stoppingToken) { await _semaphore.WaitAsync(stoppingToken).ConfigureAwait(false); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed WaitAndProcessAsync(ProcessAsync, stoppingToken).ContinueWith(task => { if (task.IsFaulted) @@ -55,7 +57,8 @@ public async Task ExecuteAsync(CancellationToken stoppingToken) _logger.LogError(task.Exception, "Error occurred"); } return _semaphore.Release(); - }); + }, TaskScheduler.Default); +#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } } @@ -65,35 +68,37 @@ public async Task ExecuteAsync(CancellationToken stoppingToken) private async Task ProcessAsync(Stream server, CancellationToken stoppingToken) { + if (stoppingToken.IsCancellationRequested) + { + return; + } + server = TransformStream(server); using (var writer = new IpcWriter(server, _serializer, leaveOpen: true)) using (var reader = new IpcReader(server, _serializer, leaveOpen: true)) + using (IDisposable loggingScope = _logger.BeginScope(new Dictionary + { + { "threadId", Thread.CurrentThread.ManagedThreadId } + })) { try { - if (stoppingToken.IsCancellationRequested) - { - return; - } - - _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] client connected, reading request..."); + _logger.LogDebug($"Client connected, reading request..."); IpcRequest request = await reader.ReadIpcRequestAsync(stoppingToken).ConfigureAwait(false); stoppingToken.ThrowIfCancellationRequested(); - - _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] request received, invoking '{request.MethodName}'..."); + _logger.LogDebug($"Request received, invoking '{request.MethodName}'..."); IpcResponse response; using (IServiceScope scope = _serviceProvider.CreateScope()) { - response = await GetReponse(request, scope).ConfigureAwait(false); + response = await GetReponseAsync(request, scope).ConfigureAwait(false); } stoppingToken.ThrowIfCancellationRequested(); - - _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] sending response..."); + _logger.LogDebug($"Sending response..."); await writer.WriteAsync(response, stoppingToken).ConfigureAwait(false); - _logger.LogDebug($"[thread {Thread.CurrentThread.ManagedThreadId}] done."); + _logger.LogDebug($"Process finished."); } catch (Exception ex) when (!(ex is IpcServerException)) { @@ -104,7 +109,7 @@ private async Task ProcessAsync(Stream server, CancellationToken stoppingToken) } } - private async Task GetReponse(IpcRequest request, IServiceScope scope) + private async Task GetReponseAsync(IpcRequest request, IServiceScope scope) { if (request is null) { @@ -173,14 +178,16 @@ private async Task GetReponse(IpcRequest request, IServiceScope sco { @return = method.Invoke(service, args); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { return IpcResponse.Fail(ex, _options.IncludeFailureDetailsInResponse, true); } - if (@return is Task) + if (@return is Task task) { - await ((Task)@return).ConfigureAwait(false); + await task.ConfigureAwait(false); PropertyInfo resultProperty = @return.GetType().GetProperty("Result"); return IpcResponse.Success(resultProperty?.GetValue(@return)); @@ -198,13 +205,7 @@ private async Task GetReponse(IpcRequest request, IServiceScope sco } } - /// - /// Get the method that matches the requested signature - /// - /// The service call request - /// The service - /// The disambiguated service method - public static MethodInfo GetUnambiguousMethod(IpcRequest request, object service) + private static MethodInfo GetUnambiguousMethod(IpcRequest request, object service) { if (request == null || service == null) { diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs b/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs index 2228366..97b51d8 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs @@ -16,7 +16,7 @@ public IpcHostBuilder(IHostBuilder hostBuilder) { services .TryAddIpcInternalServices() - .AddHostedService(); + .AddHostedService(); }); } From 1c8919bcc23506948ba67ccdc67e353b74c3bca2 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Tue, 28 Apr 2020 10:21:15 +0200 Subject: [PATCH 23/53] Refactor/sign (#103) * refactor: remove endpoint name * ci: sign assemblies #81 Co-authored-by: Jacques Kang --- src/Directory.Build.props | 2 ++ src/IpcServiceFramework.sln | 1 + src/IpcServiceFramework.snk | Bin 0 -> 596 bytes .../IIpcEndpoint.cs | 2 -- .../NamedPipeIpcHostBuilderExtensions.cs | 8 ++++---- .../NamedPipeIpcServiceEndpoint.cs | 3 +-- .../TcpIpcEndpoint.cs | 3 +-- .../TcpIpcHostBuilderExtensions.cs | 8 ++++---- .../IpcBackgroundService.cs | 2 -- .../IpcEndpoint.cs | 4 ---- .../ContractTest.cs | 2 +- .../ContractTest.cs | 2 +- 12 files changed, 15 insertions(+), 22 deletions(-) create mode 100644 src/IpcServiceFramework.snk diff --git a/src/Directory.Build.props b/src/Directory.Build.props index dddcd4a..d1f8a90 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,6 +7,8 @@ https://github.com/jacqueskang/IpcServiceFramework https://github.com/jacqueskang/IpcServiceFramework 3.0.0 + true + ..\IpcServiceFramework.snk \ No newline at end of file diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index 811bb31..c2a8048 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\azure-pipelines-ci.yml = ..\azure-pipelines-ci.yml ..\azure-pipelines-pr.yml = ..\azure-pipelines-pr.yml Directory.Build.props = Directory.Build.props + IpcServiceFramework.snk = IpcServiceFramework.snk ..\README.md = ..\README.md EndProjectSection EndProject diff --git a/src/IpcServiceFramework.snk b/src/IpcServiceFramework.snk new file mode 100644 index 0000000000000000000000000000000000000000..2ce802c4dedd6edd104014672b13b08ad50c3a31 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097f{OixI>vGI}Ph*$g8@o9e-y+ItQ;XJo zd9eU#E-N|KD8GcHl0WGW&us3kJLI~-ig>Mc{{RHwFOdlYcwNWEs|8~DSW=u|Q@N#E z>_x@Z%#OtYZ5p4k0akvUm5LWVD*=K9LF`CoX1?G76TJ%qVJie_l+h{enSiq7Mt81b zXeJdUb+uf&Z=1)~YybRej-jKpOriE%`aV}an;jym$(9r z2t8I9mH?EryLsfrIH>fS5#bX-_eXk5@`|WsfJNO1L;BQ|V)upZY%~pP} zV|J#tZBTZo75YCd3mt2{Vu908S%+)hC$QgUY7u%8{uQbVTp9tHYd098SM6X3me6ER zECl~sR$*(q^mit`6gHK?UXh*a=N$SS-2yz<%ZnV(p39*C+M{Ur zI9UZ?QDVM_d@bsw%GU|#6@{4nNlDjf%!juzj2VMfR|I?Jcgrq`1!y(et-jCnMN8FZ ii&+5m7@Yy$Qwa6u?lI?D$fMmbDejR%{uZ(wG9zTQpCF$A literal 0 HcmV?d00001 diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs index 3776602..31f6a72 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs @@ -6,8 +6,6 @@ namespace JKang.IpcServiceFramework.Hosting { public interface IIpcEndpoint: IDisposable { - string Name { get; } - Task ExecuteAsync(CancellationToken stoppingToken); } } diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs index 28208b6..950d132 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs @@ -9,14 +9,14 @@ namespace JKang.IpcServiceFramework.Hosting public static class NamedPipeIpcHostBuilderExtensions { public static IIpcHostBuilder AddNamedPipeEndpoint(this IIpcHostBuilder builder, - string name, string pipeName) + string pipeName) where TContract : class { - return builder.AddNamedPipeEndpoint(name, pipeName, null); + return builder.AddNamedPipeEndpoint(pipeName, null); } public static IIpcHostBuilder AddNamedPipeEndpoint(this IIpcHostBuilder builder, - string name, string pipeName, Action configure) + string pipeName, Action configure) where TContract : class { var options = new NamedPipeIpcServiceEndpointOptions(pipeName); @@ -29,7 +29,7 @@ public static IIpcHostBuilder AddNamedPipeEndpoint(this IIpcHostBuild ILogger> logger = serviceProvider .GetRequiredService>>(); - return new NamedPipeIpcServiceEndpoint(name, options, serializer, valueConverter, logger, serviceProvider); + return new NamedPipeIpcServiceEndpoint(options, serializer, valueConverter, logger, serviceProvider); }); return builder; diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs index 0151810..aa94e39 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs @@ -14,13 +14,12 @@ public class NamedPipeIpcServiceEndpoint : IpcEndpoint private readonly NamedPipeIpcServiceEndpointOptions _options; public NamedPipeIpcServiceEndpoint( - string name, NamedPipeIpcServiceEndpointOptions options, IIpcMessageSerializer serializer, IValueConverter valueConverter, ILogger> logger, IServiceProvider serviceProvider) - : base(name, options, serviceProvider, serializer, valueConverter, logger) + : base(options, serviceProvider, serializer, valueConverter, logger) { _options = options; } diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs index 6a9f66d..089c2bf 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs @@ -16,13 +16,12 @@ public class TcpIpcEndpoint : IpcEndpoint private readonly TcpListener _listener; public TcpIpcEndpoint( - string name, TcpIpcEndpointOptions options, IIpcMessageSerializer serializer, IValueConverter valueConverter, ILogger> logger, IServiceProvider serviceProvider) - : base(name, options, serviceProvider, serializer, valueConverter, logger) + : base(options, serviceProvider, serializer, valueConverter, logger) { _options = options ?? throw new ArgumentNullException(nameof(options)); _listener = new TcpListener(_options.IpEndpoint, _options.Port); diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs index 53fdd7f..c96ef0b 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs @@ -10,10 +10,10 @@ namespace JKang.IpcServiceFramework.Hosting public static class TcpIpcHostBuilderExtensions { public static IIpcHostBuilder AddTcpEndpoint(this IIpcHostBuilder builder, - string name, IPAddress ipEndpoint, int port) + IPAddress ipEndpoint, int port) where TContract : class { - return builder.AddTcpEndpoint(name, options => + return builder.AddTcpEndpoint(options => { options.IpEndpoint = ipEndpoint; options.Port = port; @@ -21,7 +21,7 @@ public static IIpcHostBuilder AddTcpEndpoint(this IIpcHostBuilder bui } public static IIpcHostBuilder AddTcpEndpoint(this IIpcHostBuilder builder, - string name, Action configure) + Action configure) where TContract : class { var options = new TcpIpcEndpointOptions(); @@ -34,7 +34,7 @@ public static IIpcHostBuilder AddTcpEndpoint(this IIpcHostBuilder bui ILogger> logger = serviceProvider .GetRequiredService>>(); - return new TcpIpcEndpoint(name, options, serializer, valueConverter, logger, serviceProvider); + return new TcpIpcEndpoint(options, serializer, valueConverter, logger, serviceProvider); }); return builder; diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs b/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs index 6b6ac93..b797cb1 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs @@ -24,7 +24,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) foreach (IIpcEndpoint endpoint in _endpoints) { await endpoint.ExecuteAsync(stoppingToken).ConfigureAwait(false); - _logger.LogDebug("Endpoint '{EndpointName}' started.", endpoint.Name); } _logger.LogInformation("IPC background service started."); } @@ -34,7 +33,6 @@ public override void Dispose() foreach (IIpcEndpoint endpoint in _endpoints) { endpoint.Dispose(); - _logger.LogDebug("Endpoint '{EndpointName}' disposed.", endpoint.Name); } base.Dispose(); diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs index aa27e29..632d81a 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs @@ -24,14 +24,12 @@ public abstract class IpcEndpoint : IIpcEndpoint private readonly SemaphoreSlim _semaphore; protected IpcEndpoint( - string name, IpcEndpointOptions options, IServiceProvider serviceProvider, IIpcMessageSerializer serializer, IValueConverter valueConverter, ILogger logger) { - Name = name; _options = options ?? throw new ArgumentNullException(nameof(options)); _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); @@ -40,8 +38,6 @@ protected IpcEndpoint( _semaphore = new SemaphoreSlim(options.MaxConcurrentCalls); } - public string Name { get; } - public async Task ExecuteAsync(CancellationToken stoppingToken) { var factory = new TaskFactory(TaskScheduler.Default); diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs index 1b62039..6fcbaf3 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs @@ -27,7 +27,7 @@ public ContractTest(IpcApplicationFactory factory) .WithServiceImplementation(_ => serviceMock.Object) .WithIpcHostConfiguration(hostBuilder => { - hostBuilder.AddNamedPipeEndpoint("default", pipeName); + hostBuilder.AddNamedPipeEndpoint(pipeName); }) .CreateClient(services => { diff --git a/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs b/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs index 4f61561..34aa58c 100644 --- a/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs +++ b/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs @@ -29,7 +29,7 @@ public ContractTest(IpcApplicationFactory factory) .WithServiceImplementation(_ => serviceMock.Object) .WithIpcHostConfiguration(hostBuilder => { - hostBuilder.AddTcpEndpoint("default", IPAddress.Loopback, port); + hostBuilder.AddTcpEndpoint(IPAddress.Loopback, port); }) .CreateClient(services => { From 2a6df35a818a85fdc43cf61321f648550eaa0cab Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Tue, 28 Apr 2020 14:28:09 +0200 Subject: [PATCH 24/53] refactor: remove endpoint name (#102) Co-authored-by: Jacques Kang From a19009071461c8d4d02f7c59bba7268034de4842 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Wed, 29 Apr 2020 16:34:30 +0200 Subject: [PATCH 25/53] feat: refactor stream translator into endpoint/client options (#105) Co-authored-by: Jacques Kang --- README.md | 134 ++++++------------ doc/stream-translator.md | 5 + doc/tcp/security.md | 45 ++++++ src/IpcServiceFramework.sln | 1 - .../IpcClientOptions.cs | 10 ++ .../NamedPipeIpcClient.cs | 13 +- .../NamedPipeIpcClientOptions.cs | 7 + ...ipeIpcClientServiceCollectionExtensions.cs | 16 ++- .../TcpIpcClient.cs | 19 +-- .../TcpIpcClientOptions.cs | 6 +- ...TcpIpcClientServiceCollectionExtensions.cs | 13 +- .../IpcClient.cs | 9 +- .../IpcEndpointOptions.cs | 9 +- ...iceEndpoint.cs => NamedPipeIpcEndpoint.cs} | 10 +- .../NamedPipeIpcEndpointOptions.cs | 9 ++ .../NamedPipeIpcHostBuilderExtensions.cs | 15 +- .../NamedPipeIpcServiceEndpointOptions.cs | 14 -- .../TcpIpcEndpoint.cs | 6 +- .../IpcEndpoint.cs | 8 +- .../StreamTranslatorTest.cs | 51 +++++++ .../Fixtures/XorStream.cs | 61 ++++++++ src/tcp.md | 0 22 files changed, 303 insertions(+), 158 deletions(-) create mode 100644 doc/stream-translator.md create mode 100644 doc/tcp/security.md create mode 100644 src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs create mode 100644 src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientOptions.cs rename src/JKang.IpcServiceFramework.Hosting.NamedPipe/{NamedPipeIpcServiceEndpoint.cs => NamedPipeIpcEndpoint.cs} (80%) create mode 100644 src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpointOptions.cs delete mode 100644 src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpointOptions.cs create mode 100644 src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs create mode 100644 src/JKang.IpcServiceFramework.Testing/Fixtures/XorStream.cs create mode 100644 src/tcp.md diff --git a/README.md b/README.md index c56b521..54613d4 100644 --- a/README.md +++ b/README.md @@ -6,42 +6,19 @@ A .NET Core 3.1 based lightweight framework for efficient inter-process communic Named pipeline and TCP support out-of-the-box, extensible with other protocols. ## Usage - 1. Create an interface as service contract and package it in an assembly to be shared between server and client. - 2. Implement the service and host it in an console or web applciation. - 3. Invoke the service with framework provided proxy client. -## Downloads - -IpcServiceFramework is available via NuGet: - - - [JKang.IpcServiceFramework.Hosting.NamedPipe](https://www.nuget.org/packages/JKang.IpcServiceFramework.Hosting.NamedPipe/) - - [JKang.IpcServiceFramework.Client.NamedPipe](https://www.nuget.org/packages/JKang.IpcServiceFramework.Client.NamedPipe/) - - [JKang.IpcServiceFramework.Hosting.Tcp](https://www.nuget.org/packages/JKang.IpcServiceFramework.Hosting.Tcp/) - - [JKang.IpcServiceFramework.Client.NamedPipe](https://www.nuget.org/packages/JKang.IpcServiceFramework.Client.NamedPipe/) - -## Quick Start: + 1. Create an interface as service contract and package it in an assembly to be referenced by server and client applications, for example: -### Step 1: Create service contract -```csharp + ```csharp public interface IInterProcessService { string ReverseString(string input); } -``` -_Note: This interface is ideally to be placed in a library assembly to be shared between server and client._ - -### Step 2: Implement the server - -1. Create a console application with the following NuGet packages installed: + ``` -``` -> Install-Package Microsoft.Extensions.Hosting -> Install-Package JKang.IpcServiceFramework.Hosting.NamedPipe -``` - -2. Add an class that implements the service contract - -```csharp + 1. Implement the service in server application, for example: + + ```csharp class InterProcessService : IInterProcessService { public string ReverseString(string input) @@ -51,11 +28,18 @@ _Note: This interface is ideally to be placed in a library assembly to be shared return new string(charArray); } } -``` + ``` + + 1. Install the following NuGet packages in server application: -3. Configure and run the server + ```powershell + > Install-Package Microsoft.Extensions.Hosting + > Install-Package JKang.IpcServiceFramework.Hosting.NamedPipe + ``` -```csharp + 1. Register the service implementation and configure IPC endpoint(s): + + ```csharp class Program { public static void Main(string[] args) @@ -71,79 +55,43 @@ _Note: This interface is ideally to be placed in a library assembly to be shared }) .ConfigureIpcHost(builder => { - builder.AddNamedPipeEndpoint("endpoint1", "pipeinternal"); + // configure IPC endpoints + builder.AddNamedPipeEndpoint(pipeName: "my-pipe"); + }) + .ConfigureLogging(builder => + { + // optionally configure logging + builder.SetMinimumLevel(LogLevel.Information); }); } -``` + ``` -### Step 3: Invoke the service from client process + 1. Install the following NuGet package in client application: -1. Install the following package in client application: -``` -> Install-Package JKang.IpcServiceFramework.Client.NamedPipe -``` + ```powershell + > Install-Package JKang.IpcServiceFramework.Client.NamedPipe + ``` -2. Invoke the server + 1. Invoke the server -```csharp + ```csharp IIpcClient client = new ServiceCollection() - .AddNamedPipeIpcClient("pipeinternal") - .BuildServiceProvider() - .GetRequiredService>(); + .AddNamedPipeIpcClient("my-pipe") + .BuildServiceProvider() + .GetRequiredService>(); string output = await client.InvokeAsync(x => x.ReverseString(input)); -``` - -__Welcome to raise any issue or even provide any suggestion/PR to participate this project!__ - -## Security - -If you are running IPC channels over TCP on an untrusted network, you should consider using SSL. IpcServiceFramework supports SSL on TCP clients and hosts. - -### Generate certificates for testing + ``` -**Do not use the provided certificates in the project folder.** These are used for example purposes only. - -For testing, you can generate a self-signed certificate using the following openssl command: - - openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.cer -days 365 - -This generates a key and a certificate that can be used for testing. - -### Setting up the SSL endpoint - -The endpoint requires a PKCS12 file containing both the certificate and a corresponding private key. - -A certificate and key can be combined to a PKCS12 file for use with the server using the following command: - - openssl pkcs12 -export -in cert.cer -inkey key.pem -out server.pfx - -You will be asked for a password. - -You can import the certificate and provide it to the server endpoint using code similar to the following: - - var certificate = new X509Certificate2(@"path\to\server.pfx", "password"); - serviceHostBuilder.AddTcpEndpoint("someEndpoint", ip, port, certificate); - -See the ConsoleServer and WebServer projects for more complete examples. - -Note: for security and maintenance reasons, we do not recommend that you hard-code the certificate password. It should instead be stored in the application configuration file so that it can be easily changed. - -### Safe usage - -SSL/TLS is only secure if you use it properly. Here are some tips: - -* For production purposes, use a proper server certificate, signed by a real certificate authority (CA) or your organisation's internal CA. Do not use self-signed certificates in production. -* Do not use custom certificate validation callbacks on the client. They are hard to implement correctly and tend to result in security issues. -* Unconditionally returning true in a validation callback provides no security whatsoever against an attacker who can perform man-in-the-middle attacks. -* The callback used in the ConsoleServer project example is not secure. It checks for the correct certificate by hash but does not check its validity, expiry date, revocation status, or other important security properties. +## Downloads -### Client certificates +IpcServiceFramework is available via NuGet packages: -Client certificates are not currently supported. + - [JKang.IpcServiceFramework.Hosting.NamedPipe](https://www.nuget.org/packages/JKang.IpcServiceFramework.Hosting.NamedPipe/) + - [JKang.IpcServiceFramework.Client.NamedPipe](https://www.nuget.org/packages/JKang.IpcServiceFramework.Client.NamedPipe/) + - [JKang.IpcServiceFramework.Hosting.Tcp](https://www.nuget.org/packages/JKang.IpcServiceFramework.Hosting.Tcp/) + - [JKang.IpcServiceFramework.Client.Tcp](https://www.nuget.org/packages/JKang.IpcServiceFramework.Client.Tcp/) -## Stream translators +## FAQs -If you want to process the binary data after serialisation or before deserialisation, for example to add a custom handshake when the connection begins, you can do so using a stream translator. Host and client classes allow you to pass a `Func` stream translation callback in their constructors, which can be used to "wrap" a custom stream around the network stream. This is supported on TCP communications both with and without SSL enabled. See the `XorStream` class in the IpcServiceSample.ServiceContracts project for an example of a stream translator. -Stream translators are also useful for logging packets for debugging. See the `LoggingStream` class in the IpcServiceSample.ServiceContracts project for an example of using a stream translator to log traffic. diff --git a/doc/stream-translator.md b/doc/stream-translator.md new file mode 100644 index 0000000..ffa86f1 --- /dev/null +++ b/doc/stream-translator.md @@ -0,0 +1,5 @@ +## Stream translators + +If you want to process the binary data after serialisation or before deserialisation, for example to add a custom handshake when the connection begins, you can do so using a stream translator. Host and client classes allow you to pass a `Func` stream translation callback in their constructors, which can be used to "wrap" a custom stream around the network stream. This is supported on TCP communications both with and without SSL enabled. See the `XorStream` class in the IpcServiceSample.ServiceContracts project for an example of a stream translator. + +Stream translators are also useful for logging packets for debugging. See the `LoggingStream` class in the IpcServiceSample.ServiceContracts project for an example of using a stream translator to log traffic. diff --git a/doc/tcp/security.md b/doc/tcp/security.md new file mode 100644 index 0000000..c6bba8f --- /dev/null +++ b/doc/tcp/security.md @@ -0,0 +1,45 @@ +## Security + +If you are running IPC channels over TCP on an untrusted network, you should consider using SSL. IpcServiceFramework supports SSL on TCP clients and hosts. + +### Generate certificates for testing + +**Do not use the provided certificates in the project folder.** These are used for example purposes only. + +For testing, you can generate a self-signed certificate using the following openssl command: + + openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.cer -days 365 + +This generates a key and a certificate that can be used for testing. + +### Setting up the SSL endpoint + +The endpoint requires a PKCS12 file containing both the certificate and a corresponding private key. + +A certificate and key can be combined to a PKCS12 file for use with the server using the following command: + + openssl pkcs12 -export -in cert.cer -inkey key.pem -out server.pfx + +You will be asked for a password. + +You can import the certificate and provide it to the server endpoint using code similar to the following: + + var certificate = new X509Certificate2(@"path\to\server.pfx", "password"); + serviceHostBuilder.AddTcpEndpoint("someEndpoint", ip, port, certificate); + +See the ConsoleServer and WebServer projects for more complete examples. + +Note: for security and maintenance reasons, we do not recommend that you hard-code the certificate password. It should instead be stored in the application configuration file so that it can be easily changed. + +### Safe usage + +SSL/TLS is only secure if you use it properly. Here are some tips: + +* For production purposes, use a proper server certificate, signed by a real certificate authority (CA) or your organisation's internal CA. Do not use self-signed certificates in production. +* Do not use custom certificate validation callbacks on the client. They are hard to implement correctly and tend to result in security issues. +* Unconditionally returning true in a validation callback provides no security whatsoever against an attacker who can perform man-in-the-middle attacks. +* The callback used in the ConsoleServer project example is not secure. It checks for the correct certificate by hash but does not check its validity, expiry date, revocation status, or other important security properties. + +### Client certificates + +Client certificates are not currently supported. diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index c2a8048..c2125b0 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -10,7 +10,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\azure-pipelines-pr.yml = ..\azure-pipelines-pr.yml Directory.Build.props = Directory.Build.props IpcServiceFramework.snk = IpcServiceFramework.snk - ..\README.md = ..\README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Core.Tests", "JKang.IpcServiceFramework.Core.Tests\JKang.IpcServiceFramework.Core.Tests.csproj", "{1EC81913-883B-487C-A3FD-98A80EDE3225}" diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs b/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs new file mode 100644 index 0000000..9e8215e --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs @@ -0,0 +1,10 @@ +using System; +using System.IO; + +namespace JKang.IpcServiceFramework.Client +{ + public class IpcClientOptions + { + public Func StreamTranslator { get; set; } + } +} diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs index 07ef51f..18b9488 100644 --- a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs @@ -9,17 +9,20 @@ namespace JKang.IpcServiceFramework.Client.NamedPipe internal class NamedPipeIpcClient : IpcClient where TInterface : class { - private readonly string _pipeName; + private readonly NamedPipeIpcClientOptions _options; - public NamedPipeIpcClient(IIpcMessageSerializer serializer, IValueConverter converter, string pipeName) - : base(serializer, converter) + public NamedPipeIpcClient( + NamedPipeIpcClientOptions options, + IIpcMessageSerializer serializer, + IValueConverter converter) + : base(options, serializer, converter) { - _pipeName = pipeName; + _options = options; } protected override async Task ConnectToServerAsync(CancellationToken cancellationToken) { - var stream = new NamedPipeClientStream(".", _pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); + var stream = new NamedPipeClientStream(".", _options.PipeName, PipeDirection.InOut, PipeOptions.Asynchronous); await stream.ConnectAsync(cancellationToken).ConfigureAwait(false); return stream; } diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientOptions.cs b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientOptions.cs new file mode 100644 index 0000000..788b42d --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientOptions.cs @@ -0,0 +1,7 @@ +namespace JKang.IpcServiceFramework.Client.NamedPipe +{ + public class NamedPipeIpcClientOptions : IpcClientOptions + { + public string PipeName { get; set; } + } +} diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs index f51ec4c..a2b3af9 100644 --- a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using JKang.IpcServiceFramework.Client.NamedPipe; +using System; namespace Microsoft.Extensions.DependencyInjection { @@ -7,11 +8,24 @@ public static class NamedPipeIpcClientServiceCollectionExtensions public static IServiceCollection AddNamedPipeIpcClient( this IServiceCollection services, string pipeName) where TContract : class + { + return services.AddNamedPipeIpcClient(options => + { + options.PipeName = pipeName; + }); + } + + public static IServiceCollection AddNamedPipeIpcClient( + this IServiceCollection services, Action configure) + where TContract : class { return services.AddIpcClient((serializer, valueConverter) => { - return new NamedPipeIpcClient(serializer, valueConverter, pipeName); + var options = new NamedPipeIpcClientOptions(); + configure?.Invoke(options); + return new NamedPipeIpcClient(options, serializer, valueConverter); }); } + } } diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs index 8b72e7e..08966fd 100644 --- a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs @@ -1,7 +1,6 @@ using JKang.IpcServiceFramework.Services; using System; using System.IO; -using System.Net; using System.Net.Security; using System.Net.Sockets; using System.Threading; @@ -17,22 +16,10 @@ internal class TcpIpcClient : IpcClient, IDisposable private bool _isDisposed; public TcpIpcClient( + TcpIpcClientOptions options, IIpcMessageSerializer serializer, - IValueConverter converter, - IPAddress serverIp, - int serverPort) - : this(serializer, converter, new TcpIpcClientOptions - { - ServerIp = serverIp ?? throw new ArgumentNullException(nameof(serverIp)), - ServerPort = serverPort, - }) - { } - - public TcpIpcClient( - IIpcMessageSerializer serializer, - IValueConverter converter, - TcpIpcClientOptions options) - : base(serializer, converter) + IValueConverter converter) + : base(options, serializer, converter) { _options = options ?? throw new ArgumentNullException(nameof(options)); } diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientOptions.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientOptions.cs index c266624..30114d5 100644 --- a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientOptions.cs +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientOptions.cs @@ -3,10 +3,10 @@ namespace JKang.IpcServiceFramework.Client.Tcp { - public class TcpIpcClientOptions + public class TcpIpcClientOptions : IpcClientOptions { - public IPAddress ServerIp { get; set; } - public int ServerPort { get; set; } + public IPAddress ServerIp { get; set; } = IPAddress.Loopback; + public int ServerPort { get; set; } = 11843; public bool EnableSsl { get; set; } public string SslServerIdentity { get; set; } public RemoteCertificateValidationCallback SslValidationCallback { get; set; } diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs index 959cac6..97914cc 100644 --- a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using JKang.IpcServiceFramework.Client.Tcp; +using System; using System.Net; namespace Microsoft.Extensions.DependencyInjection @@ -9,19 +10,23 @@ public static IServiceCollection AddTcpIpcClient( this IServiceCollection services, IPAddress serverIp, int serverPort) where TContract : class { - return services.AddIpcClient((serializer, valueConverter) => + return services.AddTcpIpcClient(options => { - return new TcpIpcClient(serializer, valueConverter, serverIp, serverPort); + options.ServerIp = serverIp; + options.ServerPort = serverPort; }); } public static IServiceCollection AddTcpIpcClient( - this IServiceCollection services, TcpIpcClientOptions options) + this IServiceCollection services, Action configure) where TContract : class { + var options = new TcpIpcClientOptions(); + configure?.Invoke(options); + return services.AddIpcClient((serializer, valueConverter) => { - return new TcpIpcClient(serializer, valueConverter, options); + return new TcpIpcClient(options, serializer, valueConverter); }); } } diff --git a/src/JKang.IpcServiceFramework.Client/IpcClient.cs b/src/JKang.IpcServiceFramework.Client/IpcClient.cs index a098080..14826a2 100644 --- a/src/JKang.IpcServiceFramework.Client/IpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client/IpcClient.cs @@ -10,17 +10,20 @@ namespace JKang.IpcServiceFramework.Client { - public abstract class IpcClient: IIpcClient + public abstract class IpcClient : IIpcClient where TInterface : class { private static readonly ProxyGenerator _proxyGenerator = new ProxyGenerator(); + private readonly IpcClientOptions _options; private readonly IIpcMessageSerializer _serializer; private readonly IValueConverter _converter; protected IpcClient( + IpcClientOptions options, IIpcMessageSerializer serializer, IValueConverter converter) { + _options = options; _serializer = serializer; _converter = converter; } @@ -138,7 +141,7 @@ private static IpcRequest GetRequest(Expression exp, MyInterceptor interceptor) private async Task GetResponseAsync(IpcRequest request, CancellationToken cancellationToken) { using (Stream client = await ConnectToServerAsync(cancellationToken).ConfigureAwait(false)) - using (Stream client2 = TransformStream(client)) + using (Stream client2 = _options.StreamTranslator == null ? client : _options.StreamTranslator(client)) using (var writer = new IpcWriter(client2, _serializer, leaveOpen: true)) using (var reader = new IpcReader(client2, _serializer, leaveOpen: true)) { @@ -150,8 +153,6 @@ private async Task GetResponseAsync(IpcRequest request, Cancellatio } } - protected virtual Stream TransformStream(Stream input) => input; - private class MyInterceptor : IInterceptor { public IInvocation LastInvocation { get; private set; } diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs index d8343e0..003394c 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs @@ -1,9 +1,14 @@ -namespace JKang.IpcServiceFramework.Hosting.Abstractions +using System; +using System.IO; + +namespace JKang.IpcServiceFramework.Hosting.Abstractions { - public abstract class IpcEndpointOptions + public class IpcEndpointOptions { public int MaxConcurrentCalls { get; set; } = 4; public bool IncludeFailureDetailsInResponse { get; set; } + + public Func StreamTranslator { get; set; } } } diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs similarity index 80% rename from src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs rename to src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs index aa94e39..7671090 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs @@ -8,16 +8,16 @@ namespace JKang.IpcServiceFramework.Hosting.NamedPipe { - public class NamedPipeIpcServiceEndpoint : IpcEndpoint + public class NamedPipeIpcEndpoint : IpcEndpoint where TContract : class { - private readonly NamedPipeIpcServiceEndpointOptions _options; + private readonly NamedPipeIpcEndpointOptions _options; - public NamedPipeIpcServiceEndpoint( - NamedPipeIpcServiceEndpointOptions options, + public NamedPipeIpcEndpoint( + NamedPipeIpcEndpointOptions options, IIpcMessageSerializer serializer, IValueConverter valueConverter, - ILogger> logger, + ILogger> logger, IServiceProvider serviceProvider) : base(options, serviceProvider, serializer, valueConverter, logger) { diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpointOptions.cs new file mode 100644 index 0000000..7e2ab11 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpointOptions.cs @@ -0,0 +1,9 @@ +using JKang.IpcServiceFramework.Hosting.Abstractions; + +namespace JKang.IpcServiceFramework.Hosting.NamedPipe +{ + public class NamedPipeIpcEndpointOptions : IpcEndpointOptions + { + public string PipeName { get; set; } + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs index 950d132..618f3f5 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs @@ -12,24 +12,27 @@ public static IIpcHostBuilder AddNamedPipeEndpoint(this IIpcHostBuild string pipeName) where TContract : class { - return builder.AddNamedPipeEndpoint(pipeName, null); + return builder.AddNamedPipeEndpoint(options => + { + options.PipeName = pipeName; + }); } public static IIpcHostBuilder AddNamedPipeEndpoint(this IIpcHostBuilder builder, - string pipeName, Action configure) + Action configure) where TContract : class { - var options = new NamedPipeIpcServiceEndpointOptions(pipeName); + var options = new NamedPipeIpcEndpointOptions(); configure?.Invoke(options); builder.AddIpcEndpoint(serviceProvider => { IIpcMessageSerializer serializer = serviceProvider.GetRequiredService(); IValueConverter valueConverter = serviceProvider.GetRequiredService(); - ILogger> logger = serviceProvider - .GetRequiredService>>(); + ILogger> logger = serviceProvider + .GetRequiredService>>(); - return new NamedPipeIpcServiceEndpoint(options, serializer, valueConverter, logger, serviceProvider); + return new NamedPipeIpcEndpoint(options, serializer, valueConverter, logger, serviceProvider); }); return builder; diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpointOptions.cs deleted file mode 100644 index f345dd6..0000000 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcServiceEndpointOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using JKang.IpcServiceFramework.Hosting.Abstractions; - -namespace JKang.IpcServiceFramework.Hosting.NamedPipe -{ - public class NamedPipeIpcServiceEndpointOptions : IpcEndpointOptions - { - public NamedPipeIpcServiceEndpointOptions(string pipeName) - { - PipeName = pipeName; - } - - public string PipeName { get; } - } -} diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs index 089c2bf..8873eeb 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs @@ -40,7 +40,11 @@ protected override async Task WaitAndProcessAsync( using (TcpClient client = await _listener.AcceptTcpClientAsync().ConfigureAwait(false)) { Stream server = client.GetStream(); - server = TransformStream(server); + + if (_options.StreamTranslator != null) + { + server = _options.StreamTranslator(server); + } // if SSL is enabled, wrap the stream in an SslStream in client mode if (_options.EnableSsl) diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs index 632d81a..d559898 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs @@ -60,8 +60,6 @@ public async Task ExecuteAsync(CancellationToken stoppingToken) protected abstract Task WaitAndProcessAsync(Func process, CancellationToken stoppingToken); - protected virtual Stream TransformStream(Stream input) => input; - private async Task ProcessAsync(Stream server, CancellationToken stoppingToken) { if (stoppingToken.IsCancellationRequested) @@ -69,7 +67,11 @@ private async Task ProcessAsync(Stream server, CancellationToken stoppingToken) return; } - server = TransformStream(server); + if (_options.StreamTranslator != null) + { + server = _options.StreamTranslator(server); + } + using (var writer = new IpcWriter(server, _serializer, leaveOpen: true)) using (var reader = new IpcReader(server, _serializer, leaveOpen: true)) using (IDisposable loggingScope = _logger.BeginScope(new Dictionary diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs new file mode 100644 index 0000000..47efb96 --- /dev/null +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs @@ -0,0 +1,51 @@ +using AutoFixture.Xunit2; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.Testing; +using JKang.IpcServiceFramework.Testing.Fixtures; +using JKang.IpcServiceFramework.Testing.TestCases; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Numerics; +using System.Threading.Tasks; +using Xunit; + +namespace JKang.IpcServiceFramework.NamedPipeTests +{ + public class StreamTranslatorTest : IClassFixture> + { + private readonly ContractTestCase _testCase; + + public StreamTranslatorTest(IpcApplicationFactory factory) + { + string pipeName = Guid.NewGuid().ToString(); + var serviceMock = new Mock(); + IIpcClient client = factory + .WithServiceImplementation(_ => serviceMock.Object) + .WithIpcHostConfiguration(hostBuilder => + { + hostBuilder.AddNamedPipeEndpoint(options => + { + options.PipeName = pipeName; + options.StreamTranslator = x => new XorStream(x); + }); + }) + .CreateClient(services => + { + services.AddNamedPipeIpcClient(options => + { + options.PipeName = pipeName; + options.StreamTranslator = x => new XorStream(x); + }); + }); + _testCase = new ContractTestCase(serviceMock, client); + } + + [Theory, AutoData] + public Task StreamTranslator_HappyPath(string input, string expected) + => _testCase.StringType(input, expected); + } +} diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/XorStream.cs b/src/JKang.IpcServiceFramework.Testing/Fixtures/XorStream.cs new file mode 100644 index 0000000..6a53d3a --- /dev/null +++ b/src/JKang.IpcServiceFramework.Testing/Fixtures/XorStream.cs @@ -0,0 +1,61 @@ +using System.IO; + +namespace JKang.IpcServiceFramework.Testing.Fixtures +{ + public class XorStream : Stream + { + private readonly Stream _baseStream; + + public XorStream(Stream stream) + { + _baseStream = stream; + } + + public override bool CanRead => _baseStream.CanRead; + + public override bool CanSeek => _baseStream.CanSeek; + + public override bool CanWrite => _baseStream.CanWrite; + + public override long Length => _baseStream.Length; + + public override long Position { get => _baseStream.Position; set => _baseStream.Position = value; } + + public override void Flush() + { + _baseStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + int br = _baseStream.Read(buffer, offset, count); + for (int i = offset; i < offset + br; i++) + { + buffer[i] ^= 0xFF; + } + + return br; + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _baseStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _baseStream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + byte[] xoredBuffer = new byte[count]; + for (int i = 0; i < count; i++) + { + xoredBuffer[i] = (byte)(buffer[offset + i] ^ 0xFF); + } + + _baseStream.Write(xoredBuffer, 0, count); + } + } +} diff --git a/src/tcp.md b/src/tcp.md new file mode 100644 index 0000000..e69de29 From e883e641aac4a1e561f70d7f180ab881dfab6591 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Wed, 29 Apr 2020 16:49:07 +0200 Subject: [PATCH 26/53] refactor: update sample codes (#104) Co-authored-by: Jacques Kang --- .../IpcServiceSample.ConsoleClient.csproj | 2 +- samples/IpcServiceSample.Client/Program.cs | 21 ++++++++++--------- .../IpcServiceSample.Server.csproj | 2 +- samples/IpcServiceSample.Server/Program.cs | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj b/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj index 8a890c3..e066bbc 100644 --- a/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj +++ b/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/IpcServiceSample.Client/Program.cs b/samples/IpcServiceSample.Client/Program.cs index 9d8ab3f..f51da1f 100644 --- a/samples/IpcServiceSample.Client/Program.cs +++ b/samples/IpcServiceSample.Client/Program.cs @@ -10,18 +10,19 @@ class Program { private static async Task Main(string[] args) { - Console.WriteLine("Type a phrase and press enter:"); - string input = Console.ReadLine(); + while (true) + { + Console.WriteLine("Type a phrase and press enter or press Ctrl+C to exit:"); + string input = Console.ReadLine(); - Console.WriteLine("Invoking inter-process service..."); - IIpcClient client = new ServiceCollection() - .AddNamedPipeIpcClient("pipeinternal") - .BuildServiceProvider() - .GetRequiredService>(); - string output = await client.InvokeAsync(x => x.ReverseString(input)); + IIpcClient client = new ServiceCollection() + .AddNamedPipeIpcClient("pipeinternal") + .BuildServiceProvider() + .GetRequiredService>(); + string output = await client.InvokeAsync(x => x.ReverseString(input)); - Console.WriteLine($"Result from server: '{output}'"); - Console.WriteLine("Press any key to exit."); + Console.WriteLine($"Result from server: '{output}'.\n"); + } } } } diff --git a/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj b/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj index eab2854..51ba322 100644 --- a/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj +++ b/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/IpcServiceSample.Server/Program.cs b/samples/IpcServiceSample.Server/Program.cs index c7c3670..c894031 100644 --- a/samples/IpcServiceSample.Server/Program.cs +++ b/samples/IpcServiceSample.Server/Program.cs @@ -22,11 +22,11 @@ public static IHostBuilder CreateHostBuilder(string[] args) => }) .ConfigureIpcHost(builder => { - builder.AddNamedPipeEndpoint("endpoint1", "pipeinternal"); + builder.AddNamedPipeEndpoint("pipeinternal"); }) .ConfigureLogging(builder => { - builder.SetMinimumLevel(LogLevel.Information); + builder.SetMinimumLevel(LogLevel.Debug); }); } } From 519a71b10bcdcd7bd4bb86601be35080d299306f Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Thu, 30 Apr 2020 17:02:40 +0200 Subject: [PATCH 27/53] refactor: add ConnectionTimeout in client options (#106) * refactor: add ConnectionTimeout in client options * refactor: increase test duration marge * refactor: remove timeout test for tcp client Co-authored-by: Jacques Kang --- .../IpcClientOptions.cs | 6 ++++ .../NamedPipeIpcClient.cs | 2 +- .../TcpIpcClient.cs | 30 ++++------------ .../EdgeCaseTest.cs | 35 +++++++++++++++++++ 4 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs b/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs index 9e8215e..a1cae1c 100644 --- a/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs +++ b/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs @@ -6,5 +6,11 @@ namespace JKang.IpcServiceFramework.Client public class IpcClientOptions { public Func StreamTranslator { get; set; } + + /// + /// The number of milliseconds to wait for the server to respond before + /// the connection times out. Default value is 60000. + /// + public int ConnectionTimeout { get; set; } = 60000; } } diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs index 18b9488..a46dc2d 100644 --- a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs @@ -23,7 +23,7 @@ public NamedPipeIpcClient( protected override async Task ConnectToServerAsync(CancellationToken cancellationToken) { var stream = new NamedPipeClientStream(".", _options.PipeName, PipeDirection.InOut, PipeOptions.Asynchronous); - await stream.ConnectAsync(cancellationToken).ConfigureAwait(false); + await stream.ConnectAsync(_options.ConnectionTimeout, cancellationToken).ConfigureAwait(false); return stream; } } diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs index 08966fd..dfc4676 100644 --- a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs @@ -24,31 +24,15 @@ public TcpIpcClient( _options = options ?? throw new ArgumentNullException(nameof(options)); } - protected override async Task ConnectToServerAsync(CancellationToken cancellationToken) + protected override Task ConnectToServerAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - await _client.ConnectAsync(_options.ServerIp, _options.ServerPort).ConfigureAwait(false); - - //IAsyncResult result = _client.BeginConnect(_options.ServerIp, _options.ServerPort, null, null); - - //await Task.Run(() => - //{ - // // poll every 100ms to check cancellation request - // while (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(100), false)) - // { - // if (cancellationToken.IsCancellationRequested) - // { - // _client.EndConnect(result); - // cancellationToken.ThrowIfCancellationRequested(); - // } - // } - //}).ConfigureAwait(false); - - //cancellationToken.Register(() => - //{ - // _client.Close(); - //}); + if (!_client.ConnectAsync(_options.ServerIp, _options.ServerPort) + .Wait(_options.ConnectionTimeout, cancellationToken)) + { + throw new TimeoutException(); + } Stream stream = _client.GetStream(); @@ -73,7 +57,7 @@ protected override async Task ConnectToServerAsync(CancellationToken can stream = ssl; } - return stream; + return Task.FromResult(stream); } public void Dispose() => Dispose(true); diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs new file mode 100644 index 0000000..6e6817b --- /dev/null +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs @@ -0,0 +1,35 @@ +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Testing.Fixtures; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Xunit; + +namespace JKang.IpcServiceFramework.NamedPipeTests +{ + public class EdgeCaseTest + { + [Fact] + public async Task ServerIsOff_Timeout() + { + int timeout = 1000; // 1s + IIpcClient client = new ServiceCollection() + .AddNamedPipeIpcClient(options => + { + options.PipeName = "inexisted-pipe"; + options.ConnectionTimeout = timeout; + }) + .BuildServiceProvider() + .GetRequiredService>(); + + var sw = Stopwatch.StartNew(); + await Assert.ThrowsAsync(async () => + { + string output = await client.InvokeAsync(x => x.StringType("abc")); + }); + + Assert.True(sw.ElapsedMilliseconds < timeout * 2); // makesure timeout works with marge + } + } +} From a0a9d4fd5114049977b6096f44840aed83470d18 Mon Sep 17 00:00:00 2001 From: Jonathan Gilbert Date: Fri, 1 May 2020 03:23:52 -0500 Subject: [PATCH 28/53] Improvements to DefaultValueConverter (#89) * Improved DefaultValueConverter to avoid doing unnecessary serialization/deserialization for cases where a nullable type's underlying type supports a direct conversion (e.g. long -> Nullable), and added round-tripping of DateTime and TimeSpan values along the same lines as the existing Guid parsing. Added unit testing of the round-tripping functionality to DefaultValueConverterTest.cs. (Unit testing of the efficiency improvement on nullable types isn't possible, as the external interface hasn't changed there -- the code wasn't _wrong_ before, it was just inefficient.) * fix failing build * 2nd try to fix failing build * 3rd try to fix build Co-authored-by: Jacques Kang --- .../DefaultValueConverterTest.cs | 84 +++++++++++++++++++ .../Services/DefaultValueConverter.cs | 37 +++++++- 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/src/JKang.IpcServiceFramework.Core.Tests/DefaultValueConverterTest.cs b/src/JKang.IpcServiceFramework.Core.Tests/DefaultValueConverterTest.cs index 20e291a..789fa43 100644 --- a/src/JKang.IpcServiceFramework.Core.Tests/DefaultValueConverterTest.cs +++ b/src/JKang.IpcServiceFramework.Core.Tests/DefaultValueConverterTest.cs @@ -4,6 +4,8 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using Xunit; namespace JKang.IpcServiceFramework.Core.Tests @@ -136,5 +138,87 @@ public void TryConvert_StringToGuid(Guid expected) Assert.IsType(actual); Assert.Equal(expected, actual); } + + private T ParseTestData(string valueData) + { + if (typeof(T).GetMember(valueData).FirstOrDefault() is MemberInfo member) + { + if (member is FieldInfo field) + return (T)field.GetValue(null); + else if (member is PropertyInfo property) + return (T)property.GetValue(null); + else if (member is MethodInfo method) + return (T)method.Invoke(null, null); + } + + var parseMethod = typeof(T).GetMethod("Parse", new[] { typeof(string) }); + + return (T)parseMethod.Invoke(null, new[] { valueData }); + } + + private void PerformRoundTripTest(T value, Action assertAreEqual = null) + { + // Act + bool succeed = _sut.TryConvert(value, typeof(string), out object intermediate); + bool succeed2 = _sut.TryConvert(intermediate, typeof(T), out object final); + + // Assert + Assert.True(succeed); + Assert.True(succeed2); + + Assert.IsType(final); + + if (assertAreEqual != null) + assertAreEqual(value, (T)final); + else + Assert.Equal(value, final); + } + + [Theory] + [InlineData(nameof(Guid.Empty))] + [InlineData(nameof(Guid.NewGuid))] + public void TryConvert_RoundTripGuid(string valueData) + { + PerformRoundTripTest(ParseTestData(valueData)); + } + + [Theory] + [InlineData(nameof(TimeSpan.Zero))] + [InlineData(nameof(TimeSpan.MinValue))] + [InlineData(nameof(TimeSpan.MaxValue))] + [InlineData("-00:00:05.9167374")] + public void TryConvert_RoundTripTimeSpan(string valueData) + { + PerformRoundTripTest(ParseTestData(valueData)); + } + + [Theory] + [InlineData(nameof(DateTime.Now))] + [InlineData(nameof(DateTime.Today))] + [InlineData(nameof(DateTime.MinValue))] + [InlineData(nameof(DateTime.MaxValue))] + [InlineData("2020-02-05 3:10:27 PM")] + public void TryConvert_RoundTripDateTime(string valueData) + { + PerformRoundTripTest(ParseTestData(valueData), assertAreEqual: (x, y) => Assert.Equal(DateTime.SpecifyKind(x, DateTimeKind.Unspecified), DateTime.SpecifyKind(y, DateTimeKind.Unspecified))); + } + + public interface IComplexType + { + int Int32Value { get; } + string StringValue { get; } + } + + public class ComplexType : IComplexType + { + public int Int32Value { get; set; } + public string StringValue { get; set; } + } + + public enum EnumType + { + FirstOption, + SecondOption + } } } diff --git a/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs b/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs index 20febea..b1ea034 100644 --- a/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs +++ b/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs @@ -13,13 +13,18 @@ public bool TryConvert(object origValue, Type destType, out object destValue) { throw new ArgumentNullException(nameof(destType)); } + + var destConcreteType = Nullable.GetUnderlyingType(destType); if (origValue == null) { destValue = null; - return destType.IsClass || (Nullable.GetUnderlyingType(destType) != null); + return destType.IsClass || (destConcreteType != null); } + if (destConcreteType != null) + destType = destConcreteType; + if (destType.IsAssignableFrom(origValue.GetType())) { // copy value directly if it can be assigned to destType @@ -58,15 +63,39 @@ ex is ArgumentException } } - if (origValue is string str2 && destType == typeof(Guid)) + if (origValue is string origStringValue) { - if (Guid.TryParse(str2, out Guid result)) + if ((destType == typeof(Guid)) && Guid.TryParse(origStringValue, out var guidResult)) + { + destValue = guidResult; + return true; + } + + if ((destType == typeof(TimeSpan)) && TimeSpan.TryParse(origStringValue, CultureInfo.InvariantCulture, out var timeSpanResult)) + { + destValue = timeSpanResult; + return true; + } + + if ((destType == typeof(DateTime)) && DateTime.TryParse(origStringValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeResult)) { - destValue = result; + destValue = dateTimeResult; return true; } } + if ((origValue is TimeSpan timeSpan) && (destType == typeof(string))) + { + destValue = timeSpan.ToString("c"); + return true; + } + + if ((origValue is DateTime dateTime) && (destType == typeof(string))) + { + destValue = dateTime.ToString("o"); + return true; + } + if (origValue is JObject jObj) { // rely on JSON.Net to convert complexe type From 14f5fd8e3ea2ce2f2fc5719c6ceb2740d2f1f7fe Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 1 May 2020 15:36:59 +0200 Subject: [PATCH 29/53] bugfix: parameters are not deserialized properly to abstracted interface (#107) Co-authored-by: Jacques Kang --- .../Services/DefaultIpcMessageSerializer.cs | 4 ++-- .../ContractTest.cs | 4 ++++ src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs | 4 ++++ .../Fixtures/ITestDto.cs | 7 +++++++ .../Fixtures/ITestService.cs | 2 ++ .../Fixtures/TestDto.cs | 7 +++++++ .../TestCases/ContractTestCase.cs | 10 ++++++++++ 7 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/JKang.IpcServiceFramework.Testing/Fixtures/ITestDto.cs create mode 100644 src/JKang.IpcServiceFramework.Testing/Fixtures/TestDto.cs diff --git a/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs b/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs index bf43bb3..0834b85 100644 --- a/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs +++ b/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs @@ -7,7 +7,7 @@ public class DefaultIpcMessageSerializer : IIpcMessageSerializer { private static readonly JsonSerializerSettings _settings = new JsonSerializerSettings { - TypeNameHandling = TypeNameHandling.None + TypeNameHandling = TypeNameHandling.Objects }; public IpcRequest DeserializeRequest(byte[] binary) @@ -33,7 +33,7 @@ public byte[] SerializeResponse(IpcResponse response) private T Deserialize(byte[] binary) { string json = Encoding.UTF8.GetString(binary); - return JsonConvert.DeserializeObject(json); + return JsonConvert.DeserializeObject(json, _settings); } private byte[] Serialize(object obj) diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs index 6fcbaf3..6f880d9 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs @@ -80,5 +80,9 @@ public Task AsyncMethod(int expected) [Theory, AutoData] public Task ThrowException(Exception expected) => _testCase.ThrowException(expected); + + [Theory, AutoData] + public Task Abstraction(TestDto input, TestDto expected) + => _testCase.Abstraction(input, expected); } } diff --git a/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs b/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs index 34aa58c..f011750 100644 --- a/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs +++ b/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs @@ -82,5 +82,9 @@ public Task AsyncMethod(int expected) [Theory, AutoData] public Task ThrowException(Exception expected) => _testCase.ThrowException(expected); + + [Theory, AutoData] + public Task Abstraction(TestDto input, TestDto expected) + => _testCase.Abstraction(input, expected); } } diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestDto.cs b/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestDto.cs new file mode 100644 index 0000000..2e5a0cb --- /dev/null +++ b/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestDto.cs @@ -0,0 +1,7 @@ +namespace JKang.IpcServiceFramework.Testing.Fixtures +{ + public interface ITestDto + { + string Value { get; } + } +} diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs b/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs index bc3ec6f..453850c 100644 --- a/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs +++ b/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Numerics; using System.Threading.Tasks; +using Xunit.Abstractions; namespace JKang.IpcServiceFramework.Testing.Fixtures { @@ -20,5 +21,6 @@ int PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g T GenericMethod(T input); Task AsyncMethod(); void ThrowException(); + ITestDto Abstraction(ITestDto input); } } diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/TestDto.cs b/src/JKang.IpcServiceFramework.Testing/Fixtures/TestDto.cs new file mode 100644 index 0000000..13a1068 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Testing/Fixtures/TestDto.cs @@ -0,0 +1,7 @@ +namespace JKang.IpcServiceFramework.Testing.Fixtures +{ + public class TestDto : ITestDto + { + public string Value { get; set; } + } +} diff --git a/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs b/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs index 3f927b0..1aeada3 100644 --- a/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs +++ b/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs @@ -144,5 +144,15 @@ await _client Assert.Contains(expected.Message, exception.Message); Assert.DoesNotContain(IpcServerException.ServerFailureDetails, exception.ToString()); } + + public async Task Abstraction(TestDto input, TestDto expected) + { + _serviceMock + .Setup(x => x.Abstraction(It.Is(o => o.Value == input.Value))) + .Returns(expected); + + ITestDto actual = await _client.InvokeAsync(x => x.Abstraction(input)); + Assert.Equal(expected.Value, actual.Value); + } } } From dab7b167340bebcc87c6a161cd5bc2a956085011 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 2 May 2020 13:53:16 +0200 Subject: [PATCH 30/53] =?UTF-8?q?refactor:=20close=20tcp=20connection=20an?= =?UTF-8?q?d=20throw=20approriate=20exceptions=20when=20c=E2=80=A6=20(#108?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: close tcp connection and throw approriate exceptions when connection is cancelled or timed out * refactor: provide tests for tcp client cancellation and timeout Co-authored-by: Jacques Kang --- .../TcpIpcClient.cs | 2 + .../EdgeCaseTest.cs | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs index dfc4676..bbc934b 100644 --- a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs @@ -31,6 +31,8 @@ protected override Task ConnectToServerAsync(CancellationToken cancellat if (!_client.ConnectAsync(_options.ServerIp, _options.ServerPort) .Wait(_options.ConnectionTimeout, cancellationToken)) { + _client.Close(); + cancellationToken.ThrowIfCancellationRequested(); throw new TimeoutException(); } diff --git a/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs new file mode 100644 index 0000000..09e9e3d --- /dev/null +++ b/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs @@ -0,0 +1,64 @@ +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Testing.Fixtures; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace JKang.IpcServiceFramework.TcpTests +{ + public class EdgeCaseTest + { + [Fact] + public async Task ConnectionTimeout_Throw() + { + int timeout = 3000; // 3s + IIpcClient client = new ServiceCollection() + .AddTcpIpcClient(options => + { + // Connect to a non-routable IP address can trigger timeout + options.ServerIp = IPAddress.Parse("10.0.0.0"); + options.ConnectionTimeout = timeout; + }) + .BuildServiceProvider() + .GetRequiredService>(); + + var sw = Stopwatch.StartNew(); + await Assert.ThrowsAsync(async () => + { + string output = await client.InvokeAsync(x => x.StringType("abc")); + }); + + Assert.True(sw.ElapsedMilliseconds < timeout * 2); // makesure timeout works with marge + } + + [Fact] + public void ConnectionCancelled_Throw() + { + IIpcClient client = new ServiceCollection() + .AddTcpIpcClient(options => + { + // Connect to a non-routable IP address can trigger timeout + options.ServerIp = IPAddress.Parse("10.0.0.0"); + }) + .BuildServiceProvider() + .GetRequiredService>(); + + using var cts = new CancellationTokenSource(); + + Task.WaitAll( + Task.Run(async () => + { + await Assert.ThrowsAsync(async () => + { + await client.InvokeAsync(x => x.ReturnVoid(), cts.Token); + }); + }), + Task.Run(() => cts.CancelAfter(1000))); + } + } +} From e52ee938a43f7d03abc10962a4152e8059d2aaf0 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 2 May 2020 14:06:56 +0200 Subject: [PATCH 31/53] Feature/cancellation (#109) * refactor: close tcp connection and throw approriate exceptions when connection is cancelled or timed out * refactor: provide tests for tcp client cancellation and timeout * refactor: provide a test for named pipe client connection cancellation Co-authored-by: Jacques Kang --- .../EdgeCaseTest.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs index 6e6817b..4e7b4c4 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; using Xunit; @@ -31,5 +32,29 @@ await Assert.ThrowsAsync(async () => Assert.True(sw.ElapsedMilliseconds < timeout * 2); // makesure timeout works with marge } + + [Fact] + public void ConnectionCancelled_Throw() + { + IIpcClient client = new ServiceCollection() + .AddNamedPipeIpcClient(options => + { + options.PipeName = "inexisted-pipe"; + }) + .BuildServiceProvider() + .GetRequiredService>(); + + using var cts = new CancellationTokenSource(); + + Task.WaitAll( + Task.Run(async () => + { + await Assert.ThrowsAsync(async () => + { + await client.InvokeAsync(x => x.ReturnVoid(), cts.Token); + }); + }), + Task.Run(() => cts.CancelAfter(1000))); + } } } From b113685ab6d2d8fce23895e486174ed26ef0a837 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 2 May 2020 18:47:03 +0200 Subject: [PATCH 32/53] Feature/refacto (#110) * refactor: close tcp connection and throw approriate exceptions when connection is cancelled or timed out * refactor: provide tests for tcp client cancellation and timeout * refactor: provide a test for named pipe client connection cancellation * refactor: stop duplicate tests for tcp and namedpipe using shared test case Co-authored-by: Jacques Kang --- .../ContractTest.cs | 139 +++++++++++---- .../EdgeCaseTest.cs | 45 +++-- .../ErrorTest.cs | 90 ++++++++++ .../StreamTranslatorTest.cs | 32 ++-- .../ContractTest.cs | 90 ---------- .../HappyPathTest.cs | 49 ++++++ .../TestCases/ContractTestCase.cs | 158 ------------------ 7 files changed, 295 insertions(+), 308 deletions(-) create mode 100644 src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs delete mode 100644 src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs create mode 100644 src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs delete mode 100644 src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs index 6f880d9..1a9d6c4 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs @@ -3,7 +3,6 @@ using JKang.IpcServiceFramework.Hosting; using JKang.IpcServiceFramework.Testing; using JKang.IpcServiceFramework.Testing.Fixtures; -using JKang.IpcServiceFramework.Testing.TestCases; using Microsoft.Extensions.DependencyInjection; using Moq; using System; @@ -17,14 +16,14 @@ namespace JKang.IpcServiceFramework.NamedPipeTests { public class ContractTest : IClassFixture> { - private readonly ContractTestCase _testCase; + private readonly Mock _serviceMock = new Mock(); + private readonly IIpcClient _client; public ContractTest(IpcApplicationFactory factory) { string pipeName = Guid.NewGuid().ToString(); - var serviceMock = new Mock(); - IIpcClient client = factory - .WithServiceImplementation(_ => serviceMock.Object) + _client = factory + .WithServiceImplementation(_ => _serviceMock.Object) .WithIpcHostConfiguration(hostBuilder => { hostBuilder.AddNamedPipeEndpoint(pipeName); @@ -33,56 +32,134 @@ public ContractTest(IpcApplicationFactory factory) { services.AddNamedPipeIpcClient(pipeName); }); - _testCase = new ContractTestCase(serviceMock, client); } [Theory, AutoData] - public Task PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g, int h, uint i, + public async Task PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g, int h, uint i, long j, ulong k, short l, ushort m, int expected) - => _testCase.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m, expected); + { + _serviceMock + .Setup(x => x.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m)) + .Returns(expected); + + int actual = await _client + .InvokeAsync(x => x.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m)); + + Assert.Equal(expected, actual); + } [Theory, AutoData] - public Task StringType(string input, string expected) - => _testCase.StringType(input, expected); + public async Task StringType(string input, string expected) + { + _serviceMock + .Setup(x => x.StringType(input)) + .Returns(expected); + + string actual = await _client + .InvokeAsync(x => x.StringType(input)); + + Assert.Equal(expected, actual); + } [Theory, AutoData] - public Task ComplexType(Complex input, Complex expected) - => _testCase.ComplexType(input, expected); + public async Task ComplexType(Complex input, Complex expected) + { + _serviceMock.Setup(x => x.ComplexType(input)).Returns(expected); + + Complex actual = await _client + .InvokeAsync(x => x.ComplexType(input)); + + Assert.Equal(expected, actual); + } [Theory, AutoData] - public Task ComplexTypeArray(IEnumerable input, IEnumerable expected) - => _testCase.ComplexTypeArray(input, expected); + public async Task ComplexTypeArray(IEnumerable input, IEnumerable expected) + { + _serviceMock + .Setup(x => x.ComplexTypeArray(input)) + .Returns(expected); + + IEnumerable actual = await _client + .InvokeAsync(x => x.ComplexTypeArray(input)); + + Assert.Equal(expected, actual); + } [Fact] - public Task ReturnVoid() - => _testCase.ReturnVoid(); + public async Task ReturnVoid() + { + await _client.InvokeAsync(x => x.ReturnVoid()); + } [Theory, AutoData] - public Task DateTime(DateTime input, DateTime expected) - => _testCase.DateTime(input, expected); + public async Task DateTime(DateTime input, DateTime expected) + { + _serviceMock.Setup(x => x.DateTime(input)).Returns(expected); - [Theory, AutoData] - public Task EnumType(DateTimeStyles input, DateTimeStyles expected) - => _testCase.EnumType(input, expected); + DateTime actual = await _client + .InvokeAsync(x => x.DateTime(input)); + + Assert.Equal(expected, actual); + } [Theory, AutoData] - public Task ByteArray(byte[] input, byte[] expected) - => _testCase.ByteArray(input, expected); + public async Task EnumType(DateTimeStyles input, DateTimeStyles expected) + { + _serviceMock.Setup(x => x.EnumType(input)).Returns(expected); + + DateTimeStyles actual = await _client + .InvokeAsync(x => x.EnumType(input)); + + Assert.Equal(expected, actual); + } [Theory, AutoData] - public Task GenericMethod(decimal input, decimal expected) - => _testCase.GenericMethod(input, expected); + public async Task ByteArray(byte[] input, byte[] expected) + { + _serviceMock.Setup(x => x.ByteArray(input)).Returns(expected); + + byte[] actual = await _client + .InvokeAsync(x => x.ByteArray(input)); + + Assert.Equal(expected, actual); + } [Theory, AutoData] - public Task AsyncMethod(int expected) - => _testCase.AsyncMethod(expected); + public async Task GenericMethod(decimal input, decimal expected) + { + _serviceMock + .Setup(x => x.GenericMethod(input)) + .Returns(expected); + + decimal actual = await _client + .InvokeAsync(x => x.GenericMethod(input)); + + Assert.Equal(expected, actual); + } [Theory, AutoData] - public Task ThrowException(Exception expected) - => _testCase.ThrowException(expected); + public async Task AsyncMethod(int expected) + { + _serviceMock + .Setup(x => x.AsyncMethod()) + .ReturnsAsync(expected); + + int actual = await _client + .InvokeAsync(x => x.AsyncMethod()); + + Assert.Equal(expected, actual); + } [Theory, AutoData] - public Task Abstraction(TestDto input, TestDto expected) - => _testCase.Abstraction(input, expected); + public async Task Abstraction(TestDto input, TestDto expected) + { + _serviceMock + .Setup(x => x.Abstraction(It.Is(o => o.Value == input.Value))) + .Returns(expected); + + ITestDto actual = await _client.InvokeAsync(x => x.Abstraction(input)); + + Assert.Equal(expected.Value, actual.Value); + } } } diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs index 4e7b4c4..a87f46e 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs @@ -1,28 +1,41 @@ -using JKang.IpcServiceFramework.Client; +using AutoFixture.Xunit2; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.Testing; using JKang.IpcServiceFramework.Testing.Fixtures; using Microsoft.Extensions.DependencyInjection; +using Moq; using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Xunit; + namespace JKang.IpcServiceFramework.NamedPipeTests { - public class EdgeCaseTest + public class EdgeCaseTest : IClassFixture> { + private readonly IpcApplicationFactory _factory; + + public EdgeCaseTest(IpcApplicationFactory factory) + { + _factory = factory; + } + [Fact] public async Task ServerIsOff_Timeout() { int timeout = 1000; // 1s - IIpcClient client = new ServiceCollection() - .AddNamedPipeIpcClient(options => + IIpcClient client = _factory + .CreateClient(services => { - options.PipeName = "inexisted-pipe"; - options.ConnectionTimeout = timeout; - }) - .BuildServiceProvider() - .GetRequiredService>(); + services.AddNamedPipeIpcClient(options => + { + options.PipeName = "inexisted-pipe"; + options.ConnectionTimeout = timeout; + }); + }); var sw = Stopwatch.StartNew(); await Assert.ThrowsAsync(async () => @@ -36,13 +49,15 @@ await Assert.ThrowsAsync(async () => [Fact] public void ConnectionCancelled_Throw() { - IIpcClient client = new ServiceCollection() - .AddNamedPipeIpcClient(options => + IIpcClient client = _factory + .CreateClient(services => { - options.PipeName = "inexisted-pipe"; - }) - .BuildServiceProvider() - .GetRequiredService>(); + services.AddNamedPipeIpcClient(options => + { + options.PipeName = "inexisted-pipe"; + options.ConnectionTimeout = Timeout.Infinite; + }); + }); using var cts = new CancellationTokenSource(); diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs new file mode 100644 index 0000000..9c8d570 --- /dev/null +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs @@ -0,0 +1,90 @@ +using AutoFixture.Xunit2; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.Testing; +using JKang.IpcServiceFramework.Testing.Fixtures; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + + +namespace JKang.IpcServiceFramework.NamedPipeTests +{ + public class ErrorTest : IClassFixture> + { + private readonly Mock _serviceMock = new Mock(); + private readonly IpcApplicationFactory _factory; + + public ErrorTest(IpcApplicationFactory factory) + { + _factory = factory + .WithServiceImplementation(_ => _serviceMock.Object); + } + + [Theory, AutoData] + public async Task Exception_ThrowWithoutDetails(string pipeName, string details) + { + _serviceMock.Setup(x => x.ThrowException()) + .Throws(new Exception(details)); + + IIpcClient client = _factory + .WithIpcHostConfiguration(hostBuilder => + { + hostBuilder.AddNamedPipeEndpoint(options => + { + options.PipeName = pipeName; + options.IncludeFailureDetailsInResponse = false; + }); + }) + .CreateClient(services => + { + services.AddNamedPipeIpcClient(options => + { + options.PipeName = pipeName; + }); + }); + + IpcServerUserCodeException actual = await Assert.ThrowsAsync(async () => + { + await client.InvokeAsync(x => x.ThrowException()); + }); + + Assert.Null(actual.FailureDetails); + } + + [Theory, AutoData] + public async Task Exception_ThrowWithDetails(string pipeName, string details) + { + _serviceMock.Setup(x => x.ThrowException()) + .Throws(new Exception(details)); + + IIpcClient client = _factory + .WithIpcHostConfiguration(hostBuilder => + { + hostBuilder.AddNamedPipeEndpoint(options => + { + options.PipeName = pipeName; + options.IncludeFailureDetailsInResponse = true; + }); + }) + .CreateClient(services => + { + services.AddNamedPipeIpcClient(options => + { + options.PipeName = pipeName; + }); + }); + + IpcServerUserCodeException actual = await Assert.ThrowsAsync(async () => + { + await client.InvokeAsync(x => x.ThrowException()); + }); + + Assert.Contains(details, actual.FailureDetails); + } + } +} diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs index 47efb96..70d20e2 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs @@ -3,13 +3,9 @@ using JKang.IpcServiceFramework.Hosting; using JKang.IpcServiceFramework.Testing; using JKang.IpcServiceFramework.Testing.Fixtures; -using JKang.IpcServiceFramework.Testing.TestCases; using Microsoft.Extensions.DependencyInjection; using Moq; using System; -using System.Collections.Generic; -using System.Globalization; -using System.Numerics; using System.Threading.Tasks; using Xunit; @@ -17,14 +13,23 @@ namespace JKang.IpcServiceFramework.NamedPipeTests { public class StreamTranslatorTest : IClassFixture> { - private readonly ContractTestCase _testCase; + private readonly Mock _serviceMock = new Mock(); + private readonly IpcApplicationFactory _factory; public StreamTranslatorTest(IpcApplicationFactory factory) { - string pipeName = Guid.NewGuid().ToString(); - var serviceMock = new Mock(); - IIpcClient client = factory - .WithServiceImplementation(_ => serviceMock.Object) + _factory = factory; + } + + [Theory, AutoData] + public async Task StreamTranslator_HappyPath(string pipeName, string input, string expected) + { + _serviceMock + .Setup(x => x.StringType(input)) + .Returns(expected); + + IIpcClient client = _factory + .WithServiceImplementation(_ => _serviceMock.Object) .WithIpcHostConfiguration(hostBuilder => { hostBuilder.AddNamedPipeEndpoint(options => @@ -41,11 +46,10 @@ public StreamTranslatorTest(IpcApplicationFactory factory) options.StreamTranslator = x => new XorStream(x); }); }); - _testCase = new ContractTestCase(serviceMock, client); - } - [Theory, AutoData] - public Task StreamTranslator_HappyPath(string input, string expected) - => _testCase.StringType(input, expected); + string actual = await client.InvokeAsync(x => x.StringType(input)); + + Assert.Equal(expected, actual); + } } } diff --git a/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs b/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs deleted file mode 100644 index f011750..0000000 --- a/src/JKang.IpcServiceFramework.TcpTests/ContractTest.cs +++ /dev/null @@ -1,90 +0,0 @@ -using AutoFixture.Xunit2; -using JKang.IpcServiceFramework.Client; -using JKang.IpcServiceFramework.Hosting; -using JKang.IpcServiceFramework.Testing; -using JKang.IpcServiceFramework.Testing.Fixtures; -using JKang.IpcServiceFramework.Testing.TestCases; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Net; -using System.Numerics; -using System.Threading.Tasks; -using Xunit; - -namespace JKang.IpcServiceFramework.TcpTests -{ - public class ContractTest : IClassFixture> - { - private static readonly Random _rand = new Random(); - private readonly ContractTestCase _testCase; - - public ContractTest(IpcApplicationFactory factory) - { - int port = _rand.Next(10000, 50000); - var serviceMock = new Mock(); - IIpcClient client = factory - .WithServiceImplementation(_ => serviceMock.Object) - .WithIpcHostConfiguration(hostBuilder => - { - hostBuilder.AddTcpEndpoint(IPAddress.Loopback, port); - }) - .CreateClient(services => - { - services.AddTcpIpcClient(IPAddress.Loopback, port); - }); - _testCase = new ContractTestCase(serviceMock, client); - } - - [Theory, AutoData] - public Task PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g, int h, uint i, - long j, ulong k, short l, ushort m, int expected) - => _testCase.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m, expected); - - [Theory, AutoData] - public Task StringType(string input, string expected) - => _testCase.StringType(input, expected); - - [Theory, AutoData] - public Task ComplexType(Complex input, Complex expected) - => _testCase.ComplexType(input, expected); - - [Theory, AutoData] - public Task ComplexTypeArray(IEnumerable input, IEnumerable expected) - => _testCase.ComplexTypeArray(input, expected); - - [Fact] - public Task ReturnVoid() - => _testCase.ReturnVoid(); - - [Theory, AutoData] - public Task DateTime(DateTime input, DateTime expected) - => _testCase.DateTime(input, expected); - - [Theory, AutoData] - public Task EnumType(DateTimeStyles input, DateTimeStyles expected) - => _testCase.EnumType(input, expected); - - [Theory, AutoData] - public Task ByteArray(byte[] input, byte[] expected) - => _testCase.ByteArray(input, expected); - - [Theory, AutoData] - public Task GenericMethod(decimal input, decimal expected) - => _testCase.GenericMethod(input, expected); - - [Theory, AutoData] - public Task AsyncMethod(int expected) - => _testCase.AsyncMethod(expected); - - [Theory, AutoData] - public Task ThrowException(Exception expected) - => _testCase.ThrowException(expected); - - [Theory, AutoData] - public Task Abstraction(TestDto input, TestDto expected) - => _testCase.Abstraction(input, expected); - } -} diff --git a/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs b/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs new file mode 100644 index 0000000..b26f694 --- /dev/null +++ b/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs @@ -0,0 +1,49 @@ +using AutoFixture.Xunit2; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.Testing; +using JKang.IpcServiceFramework.Testing.Fixtures; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using System; +using System.Net; +using System.Threading.Tasks; +using Xunit; + +namespace JKang.IpcServiceFramework.TcpTests +{ + public class HappyPathTest : IClassFixture> + { + private static readonly Random _rand = new Random(); + private readonly Mock _serviceMock = new Mock(); + private readonly IIpcClient _client; + + public HappyPathTest(IpcApplicationFactory factory) + { + int port = _rand.Next(10000, 50000); + _client = factory + .WithServiceImplementation(_ => _serviceMock.Object) + .WithIpcHostConfiguration(hostBuilder => + { + hostBuilder.AddTcpEndpoint(IPAddress.Loopback, port); + }) + .CreateClient(services => + { + services.AddTcpIpcClient(IPAddress.Loopback, port); + }); + } + + [Theory, AutoData] + public async Task HappyPath(string input, string expected) + { + _serviceMock + .Setup(x => x.StringType(input)) + .Returns(expected); + + string actual = await _client + .InvokeAsync(x => x.StringType(input)); + + Assert.Equal(expected, actual); + } + } +} diff --git a/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs b/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs deleted file mode 100644 index 1aeada3..0000000 --- a/src/JKang.IpcServiceFramework.Testing/TestCases/ContractTestCase.cs +++ /dev/null @@ -1,158 +0,0 @@ -using JKang.IpcServiceFramework.Client; -using JKang.IpcServiceFramework.Testing.Fixtures; -using Moq; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Numerics; -using System.Threading.Tasks; -using Xunit; - -namespace JKang.IpcServiceFramework.Testing.TestCases -{ - public class ContractTestCase - { - private readonly Mock _serviceMock; - private readonly IIpcClient _client; - - public ContractTestCase( - Mock serviceMock, - IIpcClient client) - { - _serviceMock = serviceMock; - _client = client; - } - - public async Task PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g, int h, uint i, - long j, ulong k, short l, ushort m, int expected) - { - _serviceMock - .Setup(x => x.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m)) - .Returns(expected); - - int actual = await _client - .InvokeAsync(x => x.PrimitiveTypes(a, b, c, d, e, f, g, h, i, j, k, l, m)); - - Assert.Equal(expected, actual); - } - - public async Task StringType(string input, string expected) - { - _serviceMock - .Setup(x => x.StringType(input)) - .Returns(expected); - - string actual = await _client - .InvokeAsync(x => x.StringType(input)); - - Assert.Equal(expected, actual); - } - - public async Task ComplexType(Complex input, Complex expected) - { - _serviceMock.Setup(x => x.ComplexType(input)).Returns(expected); - - Complex actual = await _client - .InvokeAsync(x => x.ComplexType(input)); - - Assert.Equal(expected, actual); - } - - public async Task ComplexTypeArray(IEnumerable input, IEnumerable expected) - { - _serviceMock - .Setup(x => x.ComplexTypeArray(input)) - .Returns(expected); - - IEnumerable actual = await _client - .InvokeAsync(x => x.ComplexTypeArray(input)); - - Assert.Equal(expected, actual); - } - - public async Task ReturnVoid() - { - await _client.InvokeAsync(x => x.ReturnVoid()); - } - - public async Task DateTime(DateTime input, DateTime expected) - { - _serviceMock.Setup(x => x.DateTime(input)).Returns(expected); - - DateTime actual = await _client - .InvokeAsync(x => x.DateTime(input)); - - Assert.Equal(expected, actual); - } - - public async Task EnumType(DateTimeStyles input, DateTimeStyles expected) - { - _serviceMock.Setup(x => x.EnumType(input)).Returns(expected); - - DateTimeStyles actual = await _client - .InvokeAsync(x => x.EnumType(input)); - - Assert.Equal(expected, actual); - } - - public async Task ByteArray(byte[] input, byte[] expected) - { - _serviceMock.Setup(x => x.ByteArray(input)).Returns(expected); - - byte[] actual = await _client - .InvokeAsync(x => x.ByteArray(input)); - - Assert.Equal(expected, actual); - } - - public async Task GenericMethod(decimal input, decimal expected) - { - _serviceMock - .Setup(x => x.GenericMethod(input)) - .Returns(expected); - - decimal actual = await _client - .InvokeAsync(x => x.GenericMethod(input)); - - Assert.Equal(expected, actual); - } - - public async Task AsyncMethod(int expected) - { - _serviceMock - .Setup(x => x.AsyncMethod()) - .ReturnsAsync(expected); - - int actual = await _client - .InvokeAsync(x => x.AsyncMethod()); - - Assert.Equal(expected, actual); - } - - public async Task ThrowException(Exception expected) - { - _serviceMock - .Setup(x => x.ThrowException()) - .Throws(expected); - - IpcServerUserCodeException exception = await Assert.ThrowsAsync(async () => - { - await _client - .InvokeAsync(x => x.ThrowException()); - }); - - Assert.Contains(expected.Message, exception.Message); - Assert.DoesNotContain(IpcServerException.ServerFailureDetails, exception.ToString()); - } - - public async Task Abstraction(TestDto input, TestDto expected) - { - _serviceMock - .Setup(x => x.Abstraction(It.Is(o => o.Value == input.Value))) - .Returns(expected); - - ITestDto actual = await _client.InvokeAsync(x => x.Abstraction(input)); - Assert.Equal(expected.Value, actual.Value); - } - } -} From 397c7d547fe462a9b4183f8addb6c06deffd4dd5 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sat, 2 May 2020 19:10:34 +0200 Subject: [PATCH 33/53] refactor: remove shared testing fixtures; fix warnings (#111) Co-authored-by: Jacques Kang --- .../Services/DefaultValueConverter.cs | 16 +++++++------ .../ContractTest.cs | 2 +- .../EdgeCaseTest.cs | 7 ++---- .../ErrorTest.cs | 4 +--- .../Fixtures/ITestDto.cs | 2 +- .../Fixtures/ITestService.cs | 3 +-- .../Fixtures/TestDto.cs | 2 +- .../Fixtures/XorStream.cs | 0 .../StreamTranslatorTest.cs | 2 +- .../EdgeCaseTest.cs | 5 ++-- .../Fixtures/ITestService.cs | 7 ++++++ .../HappyPathTest.cs | 2 +- .../TestBase.cs | 23 ------------------- 13 files changed, 27 insertions(+), 48 deletions(-) rename src/{JKang.IpcServiceFramework.Testing => JKang.IpcServiceFramework.NamedPipeTests}/Fixtures/ITestDto.cs (54%) rename src/{JKang.IpcServiceFramework.Testing => JKang.IpcServiceFramework.NamedPipeTests}/Fixtures/ITestService.cs (91%) rename src/{JKang.IpcServiceFramework.Testing => JKang.IpcServiceFramework.NamedPipeTests}/Fixtures/TestDto.cs (59%) rename src/{JKang.IpcServiceFramework.Testing => JKang.IpcServiceFramework.NamedPipeTests}/Fixtures/XorStream.cs (100%) create mode 100644 src/JKang.IpcServiceFramework.TcpTests/Fixtures/ITestService.cs delete mode 100644 src/JKang.IpcServiceFramework.Testing/TestBase.cs diff --git a/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs b/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs index b1ea034..f343930 100644 --- a/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs +++ b/src/JKang.IpcServiceFramework.Core/Services/DefaultValueConverter.cs @@ -13,8 +13,8 @@ public bool TryConvert(object origValue, Type destType, out object destValue) { throw new ArgumentNullException(nameof(destType)); } - - var destConcreteType = Nullable.GetUnderlyingType(destType); + + Type destConcreteType = Nullable.GetUnderlyingType(destType); if (origValue == null) { @@ -23,7 +23,9 @@ public bool TryConvert(object origValue, Type destType, out object destValue) } if (destConcreteType != null) + { destType = destConcreteType; + } if (destType.IsAssignableFrom(origValue.GetType())) { @@ -65,19 +67,19 @@ ex is ArgumentException if (origValue is string origStringValue) { - if ((destType == typeof(Guid)) && Guid.TryParse(origStringValue, out var guidResult)) + if ((destType == typeof(Guid)) && Guid.TryParse(origStringValue, out Guid guidResult)) { destValue = guidResult; return true; } - if ((destType == typeof(TimeSpan)) && TimeSpan.TryParse(origStringValue, CultureInfo.InvariantCulture, out var timeSpanResult)) + if ((destType == typeof(TimeSpan)) && TimeSpan.TryParse(origStringValue, CultureInfo.InvariantCulture, out TimeSpan timeSpanResult)) { destValue = timeSpanResult; return true; } - if ((destType == typeof(DateTime)) && DateTime.TryParse(origStringValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeResult)) + if ((destType == typeof(DateTime)) && DateTime.TryParse(origStringValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTimeResult)) { destValue = dateTimeResult; return true; @@ -86,13 +88,13 @@ ex is ArgumentException if ((origValue is TimeSpan timeSpan) && (destType == typeof(string))) { - destValue = timeSpan.ToString("c"); + destValue = timeSpan.ToString("c", CultureInfo.InvariantCulture); return true; } if ((origValue is DateTime dateTime) && (destType == typeof(string))) { - destValue = dateTime.ToString("o"); + destValue = dateTime.ToString("o", CultureInfo.InvariantCulture); return true; } diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs index 1a9d6c4..d144ace 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs @@ -1,8 +1,8 @@ using AutoFixture.Xunit2; using JKang.IpcServiceFramework.Client; using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.NamedPipeTests.Fixtures; using JKang.IpcServiceFramework.Testing; -using JKang.IpcServiceFramework.Testing.Fixtures; using Microsoft.Extensions.DependencyInjection; using Moq; using System; diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs index a87f46e..49fe1be 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs @@ -1,10 +1,7 @@ -using AutoFixture.Xunit2; -using JKang.IpcServiceFramework.Client; -using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.NamedPipeTests.Fixtures; using JKang.IpcServiceFramework.Testing; -using JKang.IpcServiceFramework.Testing.Fixtures; using Microsoft.Extensions.DependencyInjection; -using Moq; using System; using System.Diagnostics; using System.Threading; diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs index 9c8d570..2af77dd 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs @@ -1,13 +1,11 @@ using AutoFixture.Xunit2; using JKang.IpcServiceFramework.Client; using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.NamedPipeTests.Fixtures; using JKang.IpcServiceFramework.Testing; -using JKang.IpcServiceFramework.Testing.Fixtures; using Microsoft.Extensions.DependencyInjection; using Moq; using System; -using System.Diagnostics; -using System.Threading; using System.Threading.Tasks; using Xunit; diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestDto.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestDto.cs similarity index 54% rename from src/JKang.IpcServiceFramework.Testing/Fixtures/ITestDto.cs rename to src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestDto.cs index 2e5a0cb..aff2618 100644 --- a/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestDto.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestDto.cs @@ -1,4 +1,4 @@ -namespace JKang.IpcServiceFramework.Testing.Fixtures +namespace JKang.IpcServiceFramework.NamedPipeTests.Fixtures { public interface ITestDto { diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService.cs similarity index 91% rename from src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs rename to src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService.cs index 453850c..58da492 100644 --- a/src/JKang.IpcServiceFramework.Testing/Fixtures/ITestService.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService.cs @@ -3,9 +3,8 @@ using System.Globalization; using System.Numerics; using System.Threading.Tasks; -using Xunit.Abstractions; -namespace JKang.IpcServiceFramework.Testing.Fixtures +namespace JKang.IpcServiceFramework.NamedPipeTests.Fixtures { public interface ITestService { diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/TestDto.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/TestDto.cs similarity index 59% rename from src/JKang.IpcServiceFramework.Testing/Fixtures/TestDto.cs rename to src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/TestDto.cs index 13a1068..8be9c5a 100644 --- a/src/JKang.IpcServiceFramework.Testing/Fixtures/TestDto.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/TestDto.cs @@ -1,4 +1,4 @@ -namespace JKang.IpcServiceFramework.Testing.Fixtures +namespace JKang.IpcServiceFramework.NamedPipeTests.Fixtures { public class TestDto : ITestDto { diff --git a/src/JKang.IpcServiceFramework.Testing/Fixtures/XorStream.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/XorStream.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Testing/Fixtures/XorStream.cs rename to src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/XorStream.cs diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs index 70d20e2..b4ab2b4 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs @@ -1,11 +1,11 @@ using AutoFixture.Xunit2; using JKang.IpcServiceFramework.Client; using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.NamedPipeTests.Fixtures; using JKang.IpcServiceFramework.Testing; using JKang.IpcServiceFramework.Testing.Fixtures; using Microsoft.Extensions.DependencyInjection; using Moq; -using System; using System.Threading.Tasks; using Xunit; diff --git a/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs index 09e9e3d..8b07b03 100644 --- a/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs +++ b/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs @@ -1,9 +1,8 @@ using JKang.IpcServiceFramework.Client; -using JKang.IpcServiceFramework.Testing.Fixtures; +using JKang.IpcServiceFramework.TcpTests.Fixtures; using Microsoft.Extensions.DependencyInjection; using System; using System.Diagnostics; -using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -55,7 +54,7 @@ public void ConnectionCancelled_Throw() { await Assert.ThrowsAsync(async () => { - await client.InvokeAsync(x => x.ReturnVoid(), cts.Token); + await client.InvokeAsync(x => x.StringType(string.Empty), cts.Token); }); }), Task.Run(() => cts.CancelAfter(1000))); diff --git a/src/JKang.IpcServiceFramework.TcpTests/Fixtures/ITestService.cs b/src/JKang.IpcServiceFramework.TcpTests/Fixtures/ITestService.cs new file mode 100644 index 0000000..3031e83 --- /dev/null +++ b/src/JKang.IpcServiceFramework.TcpTests/Fixtures/ITestService.cs @@ -0,0 +1,7 @@ +namespace JKang.IpcServiceFramework.TcpTests.Fixtures +{ + public interface ITestService + { + string StringType(string input); + } +} diff --git a/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs b/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs index b26f694..daa7dc8 100644 --- a/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs +++ b/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs @@ -1,8 +1,8 @@ using AutoFixture.Xunit2; using JKang.IpcServiceFramework.Client; using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.TcpTests.Fixtures; using JKang.IpcServiceFramework.Testing; -using JKang.IpcServiceFramework.Testing.Fixtures; using Microsoft.Extensions.DependencyInjection; using Moq; using System; diff --git a/src/JKang.IpcServiceFramework.Testing/TestBase.cs b/src/JKang.IpcServiceFramework.Testing/TestBase.cs deleted file mode 100644 index f4e7001..0000000 --- a/src/JKang.IpcServiceFramework.Testing/TestBase.cs +++ /dev/null @@ -1,23 +0,0 @@ -using JKang.IpcServiceFramework.Client; -using JKang.IpcServiceFramework.Testing.Fixtures; -using Moq; -using Xunit; - -namespace JKang.IpcServiceFramework.Testing -{ - public abstract class TestBase - : IClassFixture> - { - protected TestBase(IpcApplicationFactory factory) - { - Factory = factory.WithServiceImplementation(ServiceMock.Object); - } - - protected Mock ServiceMock => new Mock(); - - protected IpcApplicationFactory Factory { get; } - - protected abstract IIpcClient CreateClient( - IpcApplicationFactory factory); - } -} From 5cbf04352408f8be30e6a5b2edd3d666db49919d Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 3 May 2020 12:06:25 +0200 Subject: [PATCH 34/53] Refactor/serialization issue (#112) * refactor: refactor exception handling * refactor: override IPAddress to highlight purpose * refactor: return bad request for request deserailization and internel server error for response deserialization Co-authored-by: Jacques Kang --- .../GlobalSuppressions.cs | 10 ++ .../IpcCommunicationException.cs | 18 +++ .../IpcException.cs | 18 +++ .../IpcFaultException.cs | 29 ++++ .../IpcResponse.cs | 96 +++++------- .../IpcSerializationException.cs | 18 +++ .../IpcServerException.cs | 66 --------- .../IpcServerUserCodeException.cs | 36 ----- .../IpcStatus.cs | 10 ++ .../Services/IIpcMessageSerializer.cs | 7 + .../IpcClient.cs | 64 ++++---- .../GlobalSuppressions.cs | 10 ++ .../IO/IpcReader.cs | 44 +++--- .../Services/DefaultIpcMessageSerializer.cs | 28 +++- .../IpcEndpoint.cs | 138 +++++++++++------- .../ErrorTest.cs | 55 ++++++- .../Fixtures/ITestService.cs | 3 + .../Fixtures/UnserializableObject.cs | 14 ++ 18 files changed, 384 insertions(+), 280 deletions(-) create mode 100644 src/JKang.IpcServiceFramework.Abstractions/GlobalSuppressions.cs create mode 100644 src/JKang.IpcServiceFramework.Abstractions/IpcCommunicationException.cs create mode 100644 src/JKang.IpcServiceFramework.Abstractions/IpcException.cs create mode 100644 src/JKang.IpcServiceFramework.Abstractions/IpcFaultException.cs create mode 100644 src/JKang.IpcServiceFramework.Abstractions/IpcSerializationException.cs delete mode 100644 src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs delete mode 100644 src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs create mode 100644 src/JKang.IpcServiceFramework.Abstractions/IpcStatus.cs create mode 100644 src/JKang.IpcServiceFramework.Core/GlobalSuppressions.cs create mode 100644 src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/UnserializableObject.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/GlobalSuppressions.cs b/src/JKang.IpcServiceFramework.Abstractions/GlobalSuppressions.cs new file mode 100644 index 0000000..3736399 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Abstractions/GlobalSuppressions.cs @@ -0,0 +1,10 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "")] diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcCommunicationException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcCommunicationException.cs new file mode 100644 index 0000000..ce11717 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcCommunicationException.cs @@ -0,0 +1,18 @@ +using System; + +namespace JKang.IpcServiceFramework +{ + public class IpcCommunicationException : IpcException + { + public IpcCommunicationException() + { } + + public IpcCommunicationException(string message) + : base(message) + { } + + public IpcCommunicationException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcException.cs new file mode 100644 index 0000000..a048bfd --- /dev/null +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcException.cs @@ -0,0 +1,18 @@ +using System; + +namespace JKang.IpcServiceFramework +{ + public abstract class IpcException : Exception + { + protected IpcException() + { } + + protected IpcException(string message) + : base(message) + { } + + protected IpcException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcFaultException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcFaultException.cs new file mode 100644 index 0000000..b8bacf5 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcFaultException.cs @@ -0,0 +1,29 @@ +using System; + +namespace JKang.IpcServiceFramework +{ + /// + /// An exception that can be transfered from server to client + /// + public class IpcFaultException : IpcException + { + public IpcFaultException(IpcStatus status) + { + Status = status; + } + + public IpcFaultException(IpcStatus status, string message) + : base(message) + { + Status = status; + } + + public IpcFaultException(IpcStatus status, string message, Exception innerException) + : base(message, innerException) + { + Status = status; + } + + public IpcStatus Status { get; } + } +} diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs index 560293c..2b1296d 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs @@ -1,85 +1,65 @@ using System; -using System.Reflection; namespace JKang.IpcServiceFramework { public class IpcResponse { - public IpcResponse(bool succeed, object data, string failure, string failureDetails, bool userCodeFailure) - { - Succeed = succeed; - Data = data; - Failure = failure; - FailureDetails = failureDetails; - UserCodeFailure = userCodeFailure; - } + public static IpcResponse Success(object data) + => new IpcResponse(IpcStatus.Ok, data, null, null); - public static IpcResponse Fail(string failure) - { - return new IpcResponse(false, null, failure, null, false); - } + public static IpcResponse BadRequest() + => new IpcResponse(IpcStatus.BadRequest, null, null, null); - public static IpcResponse Fail(Exception ex, bool includeDetails, bool userFailure = false) - { - if (ex is null) - { - throw new ArgumentNullException(nameof(ex)); - } + public static IpcResponse BadRequest(string errorDetails) + => new IpcResponse(IpcStatus.BadRequest, null, errorDetails, null); - string message = null; - string details = null; + public static IpcResponse BadRequest(string errorDetails, Exception innerException) + => new IpcResponse(IpcStatus.BadRequest, null, errorDetails, innerException); - if (!userFailure) - { - message = "Internal server error: "; - } + public static IpcResponse InternalServerError() + => new IpcResponse(IpcStatus.InternalServerError, null, null, null); - message += GetFirstUsableMessage(ex); + public static IpcResponse InternalServerError(string errorDetails) + => new IpcResponse(IpcStatus.BadRequest, null, errorDetails, null); - if (includeDetails) - { - details = ex.ToString(); - } + public static IpcResponse InternalServerError(string errorDetails, Exception innerException) + => new IpcResponse(IpcStatus.BadRequest, null, errorDetails, innerException); - return new IpcResponse(false, null, message, details, userFailure); - } - - public static IpcResponse Success(object data) + public IpcResponse( + IpcStatus status, + object data, + string errorMessage, + Exception innerException) { - return new IpcResponse(true, data, null, null, false); + Status = status; + Data = data; + ErrorMessage = errorMessage; + InnerException = innerException; } - public bool Succeed { get; } + public IpcStatus Status { get; } + public object Data { get; } - public string Failure { get; } - public string FailureDetails { get; } - public bool UserCodeFailure { get; set; } - public Exception GetException() - { - if (UserCodeFailure) - { - throw new IpcServerUserCodeException(Failure, FailureDetails); - } + public string ErrorMessage { get; set; } - throw new IpcServerException(Failure, FailureDetails); - } + public Exception InnerException { get; } - private static string GetFirstUsableMessage(Exception ex) - { - Exception e = ex; + public bool Succeed() => Status == IpcStatus.Ok; - while (e != null) + /// + /// Create an exception that contains error information + /// + /// + /// If the status is doesn't represent any error + public IpcFaultException CreateFaultException() + { + if (Status <= IpcStatus.Ok) { - if (!(e is TargetInvocationException)) - { - return e.Message; - } - - e = e.InnerException; + throw new InvalidOperationException("The response doesn't contain any error"); } - return ex.Message; + return new IpcFaultException(Status, ErrorMessage, InnerException); } } } diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcSerializationException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcSerializationException.cs new file mode 100644 index 0000000..85e90cc --- /dev/null +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcSerializationException.cs @@ -0,0 +1,18 @@ +using System; + +namespace JKang.IpcServiceFramework +{ + public class IpcSerializationException : IpcException + { + public IpcSerializationException() + { } + + public IpcSerializationException(string message) + : base(message) + { } + + public IpcSerializationException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs deleted file mode 100644 index 0c4864a..0000000 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcServerException.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Runtime.Serialization; -using System.Text; - -namespace JKang.IpcServiceFramework -{ - /// - /// An exception that originated at the server. - /// - [Serializable] - public class IpcServerException : InvalidOperationException - { - public const string ServerFailureDetails = "Server failure details:"; - public string FailureDetails { get; } - - public IpcServerException() - { } - - public IpcServerException(string message) : base(message) - { } - - public IpcServerException(string message, string failureDetails) - : base(message) - { - FailureDetails = failureDetails; - } - - public IpcServerException(string message, Exception inner) - : base(message, inner) - { - } - - protected IpcServerException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - - FailureDetails = info.GetString("FailureDetails"); - } - - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - - info.AddValue("FailureDetails", FailureDetails, typeof(string)); - } - - public override string ToString() - { - if (!string.IsNullOrWhiteSpace(FailureDetails)) - { - var sb = new StringBuilder(); - sb.AppendLine(base.ToString()); - sb.AppendLine(); - sb.AppendLine(ServerFailureDetails); - sb.Append(FailureDetails); - return sb.ToString(); - } - - return base.ToString(); - } - } -} diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs deleted file mode 100644 index acf169a..0000000 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcServerUserCodeException.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace JKang.IpcServiceFramework -{ - /// - /// An exception that originated at the server, in user code. - /// - [Serializable] - public class IpcServerUserCodeException : IpcServerException - { - public IpcServerUserCodeException() - { - } - - public IpcServerUserCodeException(string message) - : base(message) - { - } - - public IpcServerUserCodeException(string message, string failureDetails) - : base(message, failureDetails) - { - } - - public IpcServerUserCodeException(string message, Exception inner) - : base(message, inner) - { - } - - protected IpcServerUserCodeException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcStatus.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcStatus.cs new file mode 100644 index 0000000..639c11b --- /dev/null +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcStatus.cs @@ -0,0 +1,10 @@ +namespace JKang.IpcServiceFramework +{ + public enum IpcStatus: int + { + Unknown = 0, + Ok = 200, + BadRequest = 400, + InternalServerError = 500, + } +} diff --git a/src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs b/src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs index c3f9a18..fa0dacc 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs @@ -2,9 +2,16 @@ { public interface IIpcMessageSerializer { + /// byte[] SerializeRequest(IpcRequest request); + + /// IpcResponse DeserializeResponse(byte[] binary); + + /// IpcRequest DeserializeRequest(byte[] binary); + + /// byte[] SerializeResponse(IpcResponse response); } } diff --git a/src/JKang.IpcServiceFramework.Client/IpcClient.cs b/src/JKang.IpcServiceFramework.Client/IpcClient.cs index 14826a2..9089954 100644 --- a/src/JKang.IpcServiceFramework.Client/IpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client/IpcClient.cs @@ -28,81 +28,79 @@ protected IpcClient( _converter = converter; } + /// If unable to serialize request + /// If communication is broken + /// If error occurred in server public async Task InvokeAsync(Expression> exp, CancellationToken cancellationToken = default) { IpcRequest request = GetRequest(exp, new MyInterceptor()); IpcResponse response = await GetResponseAsync(request, cancellationToken).ConfigureAwait(false); - if (response.Succeed) + if (!response.Succeed()) { - return; - } - else - { - throw response.GetException(); + throw response.CreateFaultException(); } } + /// If unable to serialize request + /// If communication is broken + /// If error occurred in server public async Task InvokeAsync(Expression> exp, CancellationToken cancellationToken = default) { IpcRequest request = GetRequest(exp, new MyInterceptor()); IpcResponse response = await GetResponseAsync(request, cancellationToken).ConfigureAwait(false); - if (response.Succeed) + if (!response.Succeed()) { - if (_converter.TryConvert(response.Data, typeof(TResult), out object @return)) - { - return (TResult)@return; - } - else - { - throw new InvalidOperationException($"Unable to convert returned value to '{typeof(TResult).Name}'."); - } + throw response.CreateFaultException(); } - else + + if (!_converter.TryConvert(response.Data, typeof(TResult), out object @return)) { - throw response.GetException(); + throw new IpcSerializationException($"Unable to convert returned value to '{typeof(TResult).Name}'."); } + + return (TResult)@return; } + /// If unable to serialize request + /// If communication is broken + /// If error occurred in server public async Task InvokeAsync(Expression> exp, CancellationToken cancellationToken = default) { IpcRequest request = GetRequest(exp, new MyInterceptor()); IpcResponse response = await GetResponseAsync(request, cancellationToken).ConfigureAwait(false); - if (response.Succeed) + if (!response.Succeed()) { - return; - } - else - { - throw response.GetException(); + throw response.CreateFaultException(); } } + /// If unable to serialize request + /// If communication is broken + /// If error occurred in server public async Task InvokeAsync(Expression>> exp, CancellationToken cancellationToken = default) { IpcRequest request = GetRequest(exp, new MyInterceptor>()); IpcResponse response = await GetResponseAsync(request, cancellationToken).ConfigureAwait(false); - if (response.Succeed) + if (!response.Succeed()) + { + throw response.CreateFaultException(); + } + + if (_converter.TryConvert(response.Data, typeof(TResult), out object @return)) { - if (_converter.TryConvert(response.Data, typeof(TResult), out object @return)) - { - return (TResult)@return; - } - else - { - throw new InvalidOperationException($"Unable to convert returned value to '{typeof(TResult).Name}'."); - } + return (TResult)@return; } else { - throw response.GetException(); + throw new IpcSerializationException($"Unable to convert returned value to '{typeof(TResult).Name}'."); } } diff --git a/src/JKang.IpcServiceFramework.Core/GlobalSuppressions.cs b/src/JKang.IpcServiceFramework.Core/GlobalSuppressions.cs new file mode 100644 index 0000000..3736399 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Core/GlobalSuppressions.cs @@ -0,0 +1,10 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "")] diff --git a/src/JKang.IpcServiceFramework.Core/IO/IpcReader.cs b/src/JKang.IpcServiceFramework.Core/IO/IpcReader.cs index 07ebca5..990d4bc 100644 --- a/src/JKang.IpcServiceFramework.Core/IO/IpcReader.cs +++ b/src/JKang.IpcServiceFramework.Core/IO/IpcReader.cs @@ -8,7 +8,6 @@ namespace JKang.IpcServiceFramework.IO { public class IpcReader : IDisposable { - private readonly byte[] _lengthBuffer = new byte[4]; private readonly Stream _stream; private readonly IIpcMessageSerializer _serializer; private readonly bool _leaveOpen; @@ -24,12 +23,16 @@ public IpcReader(Stream stream, IIpcMessageSerializer serializer, bool leaveOpen _leaveOpen = leaveOpen; } + /// + /// public async Task ReadIpcRequestAsync(CancellationToken cancellationToken = default) { byte[] binary = await ReadMessageAsync(cancellationToken).ConfigureAwait(false); return _serializer.DeserializeRequest(binary); } + /// + /// public async Task ReadIpcResponseAsync(CancellationToken cancellationToken = default) { byte[] binary = await ReadMessageAsync(cancellationToken).ConfigureAwait(false); @@ -38,47 +41,40 @@ public async Task ReadIpcResponseAsync(CancellationToken cancellati private async Task ReadMessageAsync(CancellationToken cancellationToken) { + byte[] lengthBuffer = new byte[4]; int headerLength = await _stream - .ReadAsync(_lengthBuffer, 0, _lengthBuffer.Length, cancellationToken) + .ReadAsync(lengthBuffer, 0, lengthBuffer.Length, cancellationToken) .ConfigureAwait(false); if (headerLength != 4) { - throw new ArgumentOutOfRangeException($"Header length must be 4 but was {headerLength}"); + throw new IpcCommunicationException($"Invalid message header length must be 4 but was {headerLength}"); } - int expectedLength = _lengthBuffer[0] | _lengthBuffer[1] << 8 | _lengthBuffer[2] << 16 | _lengthBuffer[3] << 24; - byte[] bytes = new byte[expectedLength]; - int totalBytesReceived = 0; - int remainingBytes = expectedLength; + int remainingBytes = lengthBuffer[0] | lengthBuffer[1] << 8 | lengthBuffer[2] << 16 | lengthBuffer[3] << 24; + + byte[] buffer = new byte[65536]; + int offset = 0; using (var ms = new MemoryStream()) { - while (totalBytesReceived < expectedLength) + while (remainingBytes > 0) { - int dataLength = await _stream - .ReadAsync(bytes, 0, remainingBytes, cancellationToken) + int count = Math.Min(buffer.Length, remainingBytes); + int actualCount = await _stream + .ReadAsync(buffer, offset, count, cancellationToken) .ConfigureAwait(false); - if (dataLength == 0) + if (actualCount < count) { - break; // end of stream or stream shut down. + throw new IpcCommunicationException("Stream closed unexpectedly."); } - ms.Write(bytes, 0, dataLength); - totalBytesReceived += dataLength; - remainingBytes -= dataLength; + ms.Write(buffer, 0, count); + remainingBytes -= count; } - - bytes = ms.ToArray(); - } - - if (totalBytesReceived != expectedLength) - { - throw new System.ArgumentOutOfRangeException($"Data length must be {expectedLength} but was {totalBytesReceived}"); + return ms.ToArray(); } - - return bytes; } #region IDisposible diff --git a/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs b/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs index 0834b85..dfe2da5 100644 --- a/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs +++ b/src/JKang.IpcServiceFramework.Core/Services/DefaultIpcMessageSerializer.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using System; using System.Text; namespace JKang.IpcServiceFramework.Services @@ -32,14 +33,33 @@ public byte[] SerializeResponse(IpcResponse response) private T Deserialize(byte[] binary) { - string json = Encoding.UTF8.GetString(binary); - return JsonConvert.DeserializeObject(json, _settings); + try + { + string json = Encoding.UTF8.GetString(binary); + return JsonConvert.DeserializeObject(json, _settings); + } + catch (Exception ex) when ( + ex is JsonSerializationException || + ex is ArgumentException || + ex is EncoderFallbackException) + { + throw new IpcSerializationException("Failed to deserialize IPC message", ex); + } } private byte[] Serialize(object obj) { - string json = JsonConvert.SerializeObject(obj, _settings); - return Encoding.UTF8.GetBytes(json); + try + { + string json = JsonConvert.SerializeObject(obj, _settings); + return Encoding.UTF8.GetBytes(json); + } + catch (Exception ex) when ( + ex is JsonSerializationException || + ex is EncoderFallbackException) + { + throw new IpcSerializationException("Failed to serialize IPC message", ex); + } } } } diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs index d559898..24bcda9 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs @@ -81,27 +81,79 @@ private async Task ProcessAsync(Stream server, CancellationToken stoppingToken) { try { - _logger.LogDebug($"Client connected, reading request..."); - IpcRequest request = await reader.ReadIpcRequestAsync(stoppingToken).ConfigureAwait(false); + IpcRequest request; + try + { + _logger.LogDebug($"Client connected, reading request..."); + request = await reader.ReadIpcRequestAsync(stoppingToken).ConfigureAwait(false); + } + catch (IpcSerializationException ex) + { + throw new IpcFaultException(IpcStatus.BadRequest, "Failed to deserialize request.", ex); + } stoppingToken.ThrowIfCancellationRequested(); - _logger.LogDebug($"Request received, invoking '{request.MethodName}'..."); + IpcResponse response; - using (IServiceScope scope = _serviceProvider.CreateScope()) + try + { + _logger.LogDebug($"Request received, invoking '{request.MethodName}'..."); + using (IServiceScope scope = _serviceProvider.CreateScope()) + { + response = await GetReponseAsync(request, scope).ConfigureAwait(false); + } + } + catch (Exception ex) when (!(ex is IpcException)) { - response = await GetReponseAsync(request, scope).ConfigureAwait(false); + throw new IpcFaultException(IpcStatus.InternalServerError, + "Unexpected exception raised from user code", ex); } stoppingToken.ThrowIfCancellationRequested(); - _logger.LogDebug($"Sending response..."); - await writer.WriteAsync(response, stoppingToken).ConfigureAwait(false); + + try + { + _logger.LogDebug($"Sending response..."); + await writer.WriteAsync(response, stoppingToken).ConfigureAwait(false); + } + catch (IpcSerializationException ex) + { + throw new IpcFaultException(IpcStatus.InternalServerError, + "Failed to serialize response.", ex); + } _logger.LogDebug($"Process finished."); } - catch (Exception ex) when (!(ex is IpcServerException)) + catch (IpcCommunicationException ex) { - var response = IpcResponse.Fail(ex, _options.IncludeFailureDetailsInResponse); - _logger.LogError(ex, response.Failure); + _logger.LogError(ex, "Communication error occurred."); + // if communication error occurred, client will probably not receive any response + } + catch (OperationCanceledException ex) + { + _logger.LogWarning(ex, "IPC request process cancelled"); + IpcResponse response = _options.IncludeFailureDetailsInResponse + ? IpcResponse.InternalServerError("IPC request process cancelled") + : IpcResponse.InternalServerError(); + await writer.WriteAsync(response, stoppingToken).ConfigureAwait(false); + } + catch (IpcFaultException ex) + { + _logger.LogError(ex, "Failed to process IPC request."); + IpcResponse response; + switch (ex.Status) + { + case IpcStatus.BadRequest: + response = _options.IncludeFailureDetailsInResponse + ? IpcResponse.BadRequest(ex.Message, ex.InnerException) + : IpcResponse.BadRequest(); + break; + default: + response = _options.IncludeFailureDetailsInResponse + ? IpcResponse.InternalServerError(ex.Message, ex.InnerException) + : IpcResponse.InternalServerError(); + break; + } await writer.WriteAsync(response, stoppingToken).ConfigureAwait(false); } } @@ -109,39 +161,33 @@ private async Task ProcessAsync(Stream server, CancellationToken stoppingToken) private async Task GetReponseAsync(IpcRequest request, IServiceScope scope) { - if (request is null) - { - throw new ArgumentNullException(nameof(request)); - } - - if (scope is null) - { - throw new ArgumentNullException(nameof(scope)); - } - object service = scope.ServiceProvider.GetService(); if (service == null) { - return IpcResponse.Fail($"No implementation of interface '{typeof(TContract).FullName}' found."); + throw new IpcFaultException(IpcStatus.BadRequest, + $"No implementation of interface '{typeof(TContract).FullName}' found."); } MethodInfo method = GetUnambiguousMethod(request, service); if (method == null) { - return IpcResponse.Fail($"Method '{request.MethodName}' not found in interface '{typeof(TContract).FullName}'."); + throw new IpcFaultException(IpcStatus.BadRequest, + $"Method '{request.MethodName}' not found in interface '{typeof(TContract).FullName}'."); } ParameterInfo[] paramInfos = method.GetParameters(); if (paramInfos.Length != request.Parameters.Length) { - return IpcResponse.Fail($"Parameter mismatch."); + throw new IpcFaultException(IpcStatus.BadRequest, + $"Method '{request.MethodName}' expects {paramInfos.Length} parameters."); } Type[] genericArguments = method.GetGenericArguments(); if (genericArguments.Length != request.GenericArguments.Length) { - return IpcResponse.Fail($"Generic arguments mismatch."); + throw new IpcFaultException(IpcStatus.BadRequest, + $"Generic arguments mismatch."); } object[] args = new object[paramInfos.Length]; @@ -160,46 +206,28 @@ private async Task GetReponseAsync(IpcRequest request, IServiceScop } else { - return IpcResponse.Fail($"Cannot convert value of parameter '{paramInfos[i].Name}' ({origValue}) from {origValue.GetType().Name} to {destType.Name}."); + throw new IpcFaultException(IpcStatus.BadRequest, + $"Cannot convert value of parameter '{paramInfos[i].Name}' ({origValue}) from {origValue.GetType().Name} to {destType.Name}."); } } - try + if (method.IsGenericMethod) { - if (method.IsGenericMethod) - { - method = method.MakeGenericMethod(request.GenericArguments); - } + method = method.MakeGenericMethod(request.GenericArguments); + } - object @return; - try - { - @return = method.Invoke(service, args); - } -#pragma warning disable CA1031 // Do not catch general exception types - catch (Exception ex) -#pragma warning restore CA1031 // Do not catch general exception types - { - return IpcResponse.Fail(ex, _options.IncludeFailureDetailsInResponse, true); - } + object @return = method.Invoke(service, args); - if (@return is Task task) - { - await task.ConfigureAwait(false); + if (@return is Task task) + { + await task.ConfigureAwait(false); - PropertyInfo resultProperty = @return.GetType().GetProperty("Result"); - return IpcResponse.Success(resultProperty?.GetValue(@return)); - } - else - { - return IpcResponse.Success(@return); - } + PropertyInfo resultProperty = @return.GetType().GetProperty("Result"); + return IpcResponse.Success(resultProperty?.GetValue(@return)); } - catch (Exception ex) when (!(ex is IpcServerException)) + else { - var response = IpcResponse.Fail(ex, _options.IncludeFailureDetailsInResponse); - _logger.LogError(ex, response.Failure); - return response; + return IpcResponse.Success(@return); } } diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs index 2af77dd..52853cd 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Moq; using System; +using System.Net; using System.Threading.Tasks; using Xunit; @@ -46,12 +47,12 @@ public async Task Exception_ThrowWithoutDetails(string pipeName, string details) }); }); - IpcServerUserCodeException actual = await Assert.ThrowsAsync(async () => + IpcFaultException actual = await Assert.ThrowsAsync(async () => { await client.InvokeAsync(x => x.ThrowException()); }); - Assert.Null(actual.FailureDetails); + Assert.Null(actual.InnerException); } [Theory, AutoData] @@ -77,12 +78,58 @@ public async Task Exception_ThrowWithDetails(string pipeName, string details) }); }); - IpcServerUserCodeException actual = await Assert.ThrowsAsync(async () => + IpcFaultException actual = await Assert.ThrowsAsync(async () => { await client.InvokeAsync(x => x.ThrowException()); }); - Assert.Contains(details, actual.FailureDetails); + Assert.NotNull(actual.InnerException); + Assert.NotNull(actual.InnerException.InnerException); + Assert.Equal(details, actual.InnerException.InnerException.Message); + } + + [Theory, AutoData] + public async Task UnserializableInput_ThrowSerializationException(string pipeName) + { + IIpcClient client = _factory + .WithIpcHostConfiguration(hostBuilder => + { + hostBuilder.AddNamedPipeEndpoint(pipeName); + }) + .CreateClient(services => + { + services.AddNamedPipeIpcClient(pipeName); + }); + + await Assert.ThrowsAnyAsync(async () => + { + await client.InvokeAsync(x => x.UnserializableInput(UnserializableObject.Create())); + }); + } + + [Theory, AutoData] + public async Task UnserializableOutput_ThrowFaultException(string pipeName) + { + _serviceMock + .Setup(x => x.UnserializableOutput()) + .Returns(UnserializableObject.Create()); + + IIpcClient client = _factory + .WithIpcHostConfiguration(hostBuilder => + { + hostBuilder.AddNamedPipeEndpoint(pipeName); + }) + .CreateClient(services => + { + services.AddNamedPipeIpcClient(pipeName); + }); + + IpcFaultException exception = await Assert.ThrowsAnyAsync(async () => + { + await client.InvokeAsync(x => x.UnserializableOutput()); + }); + + Assert.Equal(IpcStatus.InternalServerError, exception.Status); } } } diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService.cs index 58da492..4034c1e 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Net; using System.Numerics; using System.Threading.Tasks; @@ -21,5 +22,7 @@ int PrimitiveTypes(bool a, byte b, sbyte c, char d, decimal e, double f, float g Task AsyncMethod(); void ThrowException(); ITestDto Abstraction(ITestDto input); + void UnserializableInput(UnserializableObject input); + UnserializableObject UnserializableOutput(); } } diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/UnserializableObject.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/UnserializableObject.cs new file mode 100644 index 0000000..f065e81 --- /dev/null +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/UnserializableObject.cs @@ -0,0 +1,14 @@ +using System.Net; + +namespace JKang.IpcServiceFramework.NamedPipeTests.Fixtures +{ + public class UnserializableObject : IPAddress + { + public static UnserializableObject Create() + => new UnserializableObject(Loopback.GetAddressBytes()); + + private UnserializableObject(byte[] address) + : base(address) + { } + } +} From 42d45df5e85ff65a11d107277bde180bc71e9433 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 3 May 2020 19:29:23 +0200 Subject: [PATCH 35/53] Refactor/serialization issue (#113) * refactor: refactor exception handling * refactor: override IPAddress to highlight purpose * refactor: return bad request for request deserailization and internel server error for response deserialization * refactor: annotate IpcRequest and IpcResponse with DataContract attributes so it can be serialized with other technologies such as Protobuf * refactor: introduce IpcClientFactory to support querying multiple hosts from a same client Co-authored-by: Jacques Kang --- .../IpcRequest.cs | 25 +++++----- .../IpcResponse.cs | 6 +++ .../IIpcClient.cs | 2 + .../IIpcClientFactory.cs | 8 +++ .../NamedPipeIpcClient.cs | 10 ++-- ...ipeIpcClientServiceCollectionExtensions.cs | 21 ++++---- .../TcpIpcClient.cs | 10 ++-- ...TcpIpcClientServiceCollectionExtensions.cs | 19 ++++--- .../IpcClient.cs | 21 ++++---- .../IpcClientFactory.cs | 37 ++++++++++++++ .../IpcClientOptions.cs | 7 ++- .../IpcClientRegistration.cs | 35 +++++++++++++ .../IpcClientServiceCollectionExtensions.cs | 24 +++------ .../JKang.IpcServiceFramework.Client.csproj | 1 - .../InternalServiceCollectionExtensions.cs | 19 ------- .../IpcEndpointOptions.cs | 14 ------ ...rviceFramework.Hosting.Abstractions.csproj | 4 ++ .../NamedPipeIpcEndpoint.cs | 7 +-- .../NamedPipeIpcEndpointOptions.cs | 4 +- .../NamedPipeIpcHostBuilderExtensions.cs | 5 +- .../TcpIpcEndpoint.cs | 4 +- .../TcpIpcEndpointOptions.cs | 3 +- .../TcpIpcHostBuilderExtensions.cs | 5 +- .../IpcEndpoint.cs | 49 ++++++++++--------- .../IpcEndpointOptions.cs | 19 +++++++ .../IpcHostBuilder.cs | 1 - .../ContractTest.cs | 4 +- .../EdgeCaseTest.cs | 8 +-- .../ErrorTest.cs | 16 +++--- .../StreamTranslatorTest.cs | 4 +- .../EdgeCaseTest.cs | 42 ++++++++++------ .../HappyPathTest.cs | 4 +- .../IpcApplicationFactory.cs | 8 +-- 33 files changed, 252 insertions(+), 194 deletions(-) create mode 100644 src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClientFactory.cs create mode 100644 src/JKang.IpcServiceFramework.Client/IpcClientFactory.cs rename src/{JKang.IpcServiceFramework.Client.Abstractions => JKang.IpcServiceFramework.Client}/IpcClientOptions.cs (62%) create mode 100644 src/JKang.IpcServiceFramework.Client/IpcClientRegistration.cs delete mode 100644 src/JKang.IpcServiceFramework.Core/InternalServiceCollectionExtensions.cs delete mode 100644 src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs create mode 100644 src/JKang.IpcServiceFramework.Hosting/IpcEndpointOptions.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs index 52a2c73..fece84e 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs @@ -1,25 +1,22 @@ using System; +using System.Collections.Generic; +using System.Runtime.Serialization; namespace JKang.IpcServiceFramework { -#pragma warning disable CA1819 // Properties should not return arrays + [DataContract] public class IpcRequest { - private Type[] _genericArguments = Array.Empty(); - + [DataMember] public string MethodName { get; set; } - public object[] Parameters { get; set; } - /// - /// Gets or sets the types of parameter of the IPC method to call - /// - public Type[] ParameterTypes { get; set; } = Array.Empty(); + [DataMember] + public IEnumerable Parameters { get; set; } + + [DataMember] + public IEnumerable ParameterTypes { get; set; } - public Type[] GenericArguments - { - get => _genericArguments ?? Array.Empty(); - set => _genericArguments = value; - } + [DataMember] + public IEnumerable GenericArguments { get; set; } } -#pragma warning restore CA1819 // Properties should not return arrays } diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs b/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs index 2b1296d..79eb4d0 100644 --- a/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs +++ b/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs @@ -1,7 +1,9 @@ using System; +using System.Runtime.Serialization; namespace JKang.IpcServiceFramework { + [DataContract] public class IpcResponse { public static IpcResponse Success(object data) @@ -37,12 +39,16 @@ public IpcResponse( InnerException = innerException; } + [DataMember] public IpcStatus Status { get; } + [DataMember] public object Data { get; } + [DataMember] public string ErrorMessage { get; set; } + [DataMember] public Exception InnerException { get; } public bool Succeed() => Status == IpcStatus.Ok; diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs b/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs index 1874024..e6f9d96 100644 --- a/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs @@ -8,6 +8,8 @@ namespace JKang.IpcServiceFramework.Client public interface IIpcClient where TInterface : class { + string Name { get; } + Task InvokeAsync( Expression> exp, CancellationToken cancellationToken = default); diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClientFactory.cs b/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClientFactory.cs new file mode 100644 index 0000000..6583cc5 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClientFactory.cs @@ -0,0 +1,8 @@ +namespace JKang.IpcServiceFramework.Client +{ + public interface IIpcClientFactory + where TContract: class + { + IIpcClient CreateClient(string name); + } +} diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs index a46dc2d..b99709f 100644 --- a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClient.cs @@ -1,5 +1,4 @@ -using JKang.IpcServiceFramework.Services; -using System.IO; +using System.IO; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; @@ -12,10 +11,9 @@ internal class NamedPipeIpcClient : IpcClient private readonly NamedPipeIpcClientOptions _options; public NamedPipeIpcClient( - NamedPipeIpcClientOptions options, - IIpcMessageSerializer serializer, - IValueConverter converter) - : base(options, serializer, converter) + string name, + NamedPipeIpcClientOptions options) + : base(name, options) { _options = options; } diff --git a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs index a2b3af9..99204a1 100644 --- a/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs +++ b/src/JKang.IpcServiceFramework.Client.NamedPipe/NamedPipeIpcClientServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ -using JKang.IpcServiceFramework.Client.NamedPipe; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Client.NamedPipe; using System; namespace Microsoft.Extensions.DependencyInjection @@ -6,26 +7,24 @@ namespace Microsoft.Extensions.DependencyInjection public static class NamedPipeIpcClientServiceCollectionExtensions { public static IServiceCollection AddNamedPipeIpcClient( - this IServiceCollection services, string pipeName) + this IServiceCollection services, string name, string pipeName) where TContract : class { - return services.AddNamedPipeIpcClient(options => + return services.AddNamedPipeIpcClient(name, (_, options) => { options.PipeName = pipeName; }); } public static IServiceCollection AddNamedPipeIpcClient( - this IServiceCollection services, Action configure) + this IServiceCollection services, string name, + Action configureOptions) where TContract : class { - return services.AddIpcClient((serializer, valueConverter) => - { - var options = new NamedPipeIpcClientOptions(); - configure?.Invoke(options); - return new NamedPipeIpcClient(options, serializer, valueConverter); - }); - } + services.AddIpcClient(new IpcClientRegistration(name, + (_, options) => new NamedPipeIpcClient(name, options), configureOptions)); + return services; + } } } diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs index bbc934b..db02b00 100644 --- a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClient.cs @@ -1,5 +1,4 @@ -using JKang.IpcServiceFramework.Services; -using System; +using System; using System.IO; using System.Net.Security; using System.Net.Sockets; @@ -15,11 +14,8 @@ internal class TcpIpcClient : IpcClient, IDisposable private readonly TcpClient _client = new TcpClient(); private bool _isDisposed; - public TcpIpcClient( - TcpIpcClientOptions options, - IIpcMessageSerializer serializer, - IValueConverter converter) - : base(options, serializer, converter) + public TcpIpcClient(string name, TcpIpcClientOptions options) + : base(name, options) { _options = options ?? throw new ArgumentNullException(nameof(options)); } diff --git a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs index 97914cc..4c26970 100644 --- a/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs +++ b/src/JKang.IpcServiceFramework.Client.Tcp/TcpIpcClientServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ -using JKang.IpcServiceFramework.Client.Tcp; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Client.Tcp; using System; using System.Net; @@ -7,10 +8,10 @@ namespace Microsoft.Extensions.DependencyInjection public static class TcpIpcClientServiceCollectionExtensions { public static IServiceCollection AddTcpIpcClient( - this IServiceCollection services, IPAddress serverIp, int serverPort) + this IServiceCollection services, string name, IPAddress serverIp, int serverPort) where TContract : class { - return services.AddTcpIpcClient(options => + return services.AddTcpIpcClient(name, (_, options) => { options.ServerIp = serverIp; options.ServerPort = serverPort; @@ -18,16 +19,14 @@ public static IServiceCollection AddTcpIpcClient( } public static IServiceCollection AddTcpIpcClient( - this IServiceCollection services, Action configure) + this IServiceCollection services, string name, + Action configureOptions) where TContract : class { - var options = new TcpIpcClientOptions(); - configure?.Invoke(options); + services.AddIpcClient(new IpcClientRegistration(name, + (_, options) => new TcpIpcClient(name, options), configureOptions)); - return services.AddIpcClient((serializer, valueConverter) => - { - return new TcpIpcClient(options, serializer, valueConverter); - }); + return services; } } } diff --git a/src/JKang.IpcServiceFramework.Client/IpcClient.cs b/src/JKang.IpcServiceFramework.Client/IpcClient.cs index 9089954..64428df 100644 --- a/src/JKang.IpcServiceFramework.Client/IpcClient.cs +++ b/src/JKang.IpcServiceFramework.Client/IpcClient.cs @@ -1,6 +1,5 @@ using Castle.DynamicProxy; using JKang.IpcServiceFramework.IO; -using JKang.IpcServiceFramework.Services; using System; using System.IO; using System.Linq; @@ -15,19 +14,17 @@ public abstract class IpcClient : IIpcClient { private static readonly ProxyGenerator _proxyGenerator = new ProxyGenerator(); private readonly IpcClientOptions _options; - private readonly IIpcMessageSerializer _serializer; - private readonly IValueConverter _converter; protected IpcClient( - IpcClientOptions options, - IIpcMessageSerializer serializer, - IValueConverter converter) + string name, + IpcClientOptions options) { + Name = name; _options = options; - _serializer = serializer; - _converter = converter; } + public string Name { get; } + /// If unable to serialize request /// If communication is broken /// If error occurred in server @@ -57,7 +54,7 @@ public async Task InvokeAsync(Expression InvokeAsync(Expression GetResponseAsync(IpcRequest request, Cancellatio { using (Stream client = await ConnectToServerAsync(cancellationToken).ConfigureAwait(false)) using (Stream client2 = _options.StreamTranslator == null ? client : _options.StreamTranslator(client)) - using (var writer = new IpcWriter(client2, _serializer, leaveOpen: true)) - using (var reader = new IpcReader(client2, _serializer, leaveOpen: true)) + using (var writer = new IpcWriter(client2, _options.Serializer, leaveOpen: true)) + using (var reader = new IpcReader(client2, _options.Serializer, leaveOpen: true)) { // send request await writer.WriteAsync(request, cancellationToken).ConfigureAwait(false); diff --git a/src/JKang.IpcServiceFramework.Client/IpcClientFactory.cs b/src/JKang.IpcServiceFramework.Client/IpcClientFactory.cs new file mode 100644 index 0000000..556636e --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client/IpcClientFactory.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace JKang.IpcServiceFramework.Client +{ + internal class IpcClientFactory : IIpcClientFactory + where TContract : class + where TIpcClientOptions : IpcClientOptions + { + private readonly IServiceProvider _serviceProvider; + private readonly IEnumerable> _registrations; + + public IpcClientFactory( + IServiceProvider serviceProvider, + IEnumerable> registrations) + { + _serviceProvider = serviceProvider; + _registrations = registrations; + } + + public IIpcClient CreateClient(string name) + { + IpcClientRegistration registration = _registrations.FirstOrDefault(x => x.Name == name); + if (registration == null) + { + throw new ArgumentException($"IPC client '{name}' is not configured.", nameof(name)); + } + + using (IServiceScope scope = _serviceProvider.CreateScope()) + { + return registration.CreateClient(scope.ServiceProvider); + } + } + } +} diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs b/src/JKang.IpcServiceFramework.Client/IpcClientOptions.cs similarity index 62% rename from src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs rename to src/JKang.IpcServiceFramework.Client/IpcClientOptions.cs index a1cae1c..adefbde 100644 --- a/src/JKang.IpcServiceFramework.Client.Abstractions/IpcClientOptions.cs +++ b/src/JKang.IpcServiceFramework.Client/IpcClientOptions.cs @@ -1,4 +1,5 @@ -using System; +using JKang.IpcServiceFramework.Services; +using System; using System.IO; namespace JKang.IpcServiceFramework.Client @@ -12,5 +13,9 @@ public class IpcClientOptions /// the connection times out. Default value is 60000. /// public int ConnectionTimeout { get; set; } = 60000; + + public IIpcMessageSerializer Serializer { get; set; } = new DefaultIpcMessageSerializer(); + + public IValueConverter ValueConverter { get; set; } = new DefaultValueConverter(); } } diff --git a/src/JKang.IpcServiceFramework.Client/IpcClientRegistration.cs b/src/JKang.IpcServiceFramework.Client/IpcClientRegistration.cs new file mode 100644 index 0000000..69c3893 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Client/IpcClientRegistration.cs @@ -0,0 +1,35 @@ +using System; + +namespace JKang.IpcServiceFramework.Client +{ + public class IpcClientRegistration + where TContract: class + where TIpcClientOptions: IpcClientOptions + { + private readonly Func> _clientFactory; + private readonly Action _configureOptions; + + public IpcClientRegistration(string name, + Func> clientFactory, + Action configureOptions) + { + Name = name; + _clientFactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory)); + _configureOptions = configureOptions; + } + + public string Name { get; } + + public IIpcClient CreateClient(IServiceProvider serviceProvider) + { + if (serviceProvider is null) + { + throw new ArgumentNullException(nameof(serviceProvider)); + } + + TIpcClientOptions options = Activator.CreateInstance(); + _configureOptions?.Invoke(serviceProvider, options); + return _clientFactory.Invoke(serviceProvider, options); + } + } +} diff --git a/src/JKang.IpcServiceFramework.Client/IpcClientServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Client/IpcClientServiceCollectionExtensions.cs index 67b02bf..230b7b0 100644 --- a/src/JKang.IpcServiceFramework.Client/IpcClientServiceCollectionExtensions.cs +++ b/src/JKang.IpcServiceFramework.Client/IpcClientServiceCollectionExtensions.cs @@ -1,30 +1,20 @@ using JKang.IpcServiceFramework.Client; -using JKang.IpcServiceFramework.Services; -using System; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace Microsoft.Extensions.DependencyInjection { public static class IpcClientServiceCollectionExtensions { - public static IServiceCollection AddIpcClient(this IServiceCollection services, - Func> factory) + public static IServiceCollection AddIpcClient( + this IServiceCollection services, + IpcClientRegistration registration) where TContract : class + where TIpcClientOptions : IpcClientOptions { - if (factory is null) - { - throw new ArgumentNullException(nameof(factory)); - } - services - .TryAddIpcInternalServices() - ; + .TryAddScoped, IpcClientFactory>(); - services.AddScoped(serviceProvider => - { - IIpcMessageSerializer serializer = serviceProvider.GetRequiredService(); - IValueConverter valueConverter = serviceProvider.GetRequiredService(); - return factory(serializer, valueConverter); - }); + services.AddSingleton(registration); return services; } diff --git a/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj b/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj index abdf38b..414148f 100644 --- a/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj +++ b/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj @@ -2,7 +2,6 @@ netstandard2.0 - JKang.IpcServiceFramework ipc,interprocess,communication,wcf,client diff --git a/src/JKang.IpcServiceFramework.Core/InternalServiceCollectionExtensions.cs b/src/JKang.IpcServiceFramework.Core/InternalServiceCollectionExtensions.cs deleted file mode 100644 index eb2390a..0000000 --- a/src/JKang.IpcServiceFramework.Core/InternalServiceCollectionExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using JKang.IpcServiceFramework.Services; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class InternalServiceCollectionExtensions - { - public static IServiceCollection TryAddIpcInternalServices(this IServiceCollection services) - { - services - .TryAddScoped(); - - services - .TryAddScoped(); - - return services; - } - } -} diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs deleted file mode 100644 index 003394c..0000000 --- a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcEndpointOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.IO; - -namespace JKang.IpcServiceFramework.Hosting.Abstractions -{ - public class IpcEndpointOptions - { - public int MaxConcurrentCalls { get; set; } = 4; - - public bool IncludeFailureDetailsInResponse { get; set; } - - public Func StreamTranslator { get; set; } - } -} diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj b/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj index 969562a..3bf5359 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj +++ b/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj @@ -13,4 +13,8 @@ + + + + diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs index 7671090..fd273c2 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs @@ -1,5 +1,4 @@ -using JKang.IpcServiceFramework.Services; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using System; using System.IO; using System.IO.Pipes; @@ -15,11 +14,9 @@ public class NamedPipeIpcEndpoint : IpcEndpoint public NamedPipeIpcEndpoint( NamedPipeIpcEndpointOptions options, - IIpcMessageSerializer serializer, - IValueConverter valueConverter, ILogger> logger, IServiceProvider serviceProvider) - : base(options, serviceProvider, serializer, valueConverter, logger) + : base(options, serviceProvider, logger) { _options = options; } diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpointOptions.cs index 7e2ab11..5d9bbb6 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpointOptions.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpointOptions.cs @@ -1,6 +1,4 @@ -using JKang.IpcServiceFramework.Hosting.Abstractions; - -namespace JKang.IpcServiceFramework.Hosting.NamedPipe +namespace JKang.IpcServiceFramework.Hosting.NamedPipe { public class NamedPipeIpcEndpointOptions : IpcEndpointOptions { diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs index 618f3f5..5e04353 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcHostBuilderExtensions.cs @@ -1,5 +1,4 @@ using JKang.IpcServiceFramework.Hosting.NamedPipe; -using JKang.IpcServiceFramework.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; @@ -27,12 +26,10 @@ public static IIpcHostBuilder AddNamedPipeEndpoint(this IIpcHostBuild builder.AddIpcEndpoint(serviceProvider => { - IIpcMessageSerializer serializer = serviceProvider.GetRequiredService(); - IValueConverter valueConverter = serviceProvider.GetRequiredService(); ILogger> logger = serviceProvider .GetRequiredService>>(); - return new NamedPipeIpcEndpoint(options, serializer, valueConverter, logger, serviceProvider); + return new NamedPipeIpcEndpoint(options, logger, serviceProvider); }); return builder; diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs index 8873eeb..c7b644b 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpoint.cs @@ -17,11 +17,9 @@ public class TcpIpcEndpoint : IpcEndpoint public TcpIpcEndpoint( TcpIpcEndpointOptions options, - IIpcMessageSerializer serializer, - IValueConverter valueConverter, ILogger> logger, IServiceProvider serviceProvider) - : base(options, serviceProvider, serializer, valueConverter, logger) + : base(options, serviceProvider, logger) { _options = options ?? throw new ArgumentNullException(nameof(options)); _listener = new TcpListener(_options.IpEndpoint, _options.Port); diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpointOptions.cs index 8d567e5..938214b 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpointOptions.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcEndpointOptions.cs @@ -1,5 +1,4 @@ -using JKang.IpcServiceFramework.Hosting.Abstractions; -using System.Net; +using System.Net; using System.Security.Cryptography.X509Certificates; namespace JKang.IpcServiceFramework.Hosting.Tcp diff --git a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs index c96ef0b..53bfd4f 100644 --- a/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs +++ b/src/JKang.IpcServiceFramework.Hosting.Tcp/TcpIpcHostBuilderExtensions.cs @@ -1,5 +1,4 @@ using JKang.IpcServiceFramework.Hosting.Tcp; -using JKang.IpcServiceFramework.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; @@ -29,12 +28,10 @@ public static IIpcHostBuilder AddTcpEndpoint(this IIpcHostBuilder bui builder.AddIpcEndpoint(serviceProvider => { - IIpcMessageSerializer serializer = serviceProvider.GetRequiredService(); - IValueConverter valueConverter = serviceProvider.GetRequiredService(); ILogger> logger = serviceProvider .GetRequiredService>>(); - return new TcpIpcEndpoint(options, serializer, valueConverter, logger, serviceProvider); + return new TcpIpcEndpoint(options, logger, serviceProvider); }); return builder; diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs index 24bcda9..9e1ee81 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcEndpoint.cs @@ -1,6 +1,4 @@ -using JKang.IpcServiceFramework.Hosting.Abstractions; -using JKang.IpcServiceFramework.IO; -using JKang.IpcServiceFramework.Services; +using JKang.IpcServiceFramework.IO; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; @@ -18,22 +16,16 @@ public abstract class IpcEndpoint : IIpcEndpoint { private readonly IpcEndpointOptions _options; private readonly IServiceProvider _serviceProvider; - private readonly IIpcMessageSerializer _serializer; - private readonly IValueConverter _valueConverter; private readonly ILogger _logger; private readonly SemaphoreSlim _semaphore; protected IpcEndpoint( IpcEndpointOptions options, IServiceProvider serviceProvider, - IIpcMessageSerializer serializer, - IValueConverter valueConverter, ILogger logger) { _options = options ?? throw new ArgumentNullException(nameof(options)); _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); - _valueConverter = valueConverter ?? throw new ArgumentNullException(nameof(valueConverter)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _semaphore = new SemaphoreSlim(options.MaxConcurrentCalls); } @@ -72,8 +64,8 @@ private async Task ProcessAsync(Stream server, CancellationToken stoppingToken) server = _options.StreamTranslator(server); } - using (var writer = new IpcWriter(server, _serializer, leaveOpen: true)) - using (var reader = new IpcReader(server, _serializer, leaveOpen: true)) + using (var writer = new IpcWriter(server, _options.Serializer, leaveOpen: true)) + using (var reader = new IpcReader(server, _options.Serializer, leaveOpen: true)) using (IDisposable loggingScope = _logger.BeginScope(new Dictionary { { "threadId", Thread.CurrentThread.ManagedThreadId } @@ -177,14 +169,16 @@ private async Task GetReponseAsync(IpcRequest request, IServiceScop } ParameterInfo[] paramInfos = method.GetParameters(); - if (paramInfos.Length != request.Parameters.Length) + object[] requestParameters = request.Parameters?.ToArray() ?? Array.Empty(); + if (paramInfos.Length != requestParameters.Length) { throw new IpcFaultException(IpcStatus.BadRequest, $"Method '{request.MethodName}' expects {paramInfos.Length} parameters."); } Type[] genericArguments = method.GetGenericArguments(); - if (genericArguments.Length != request.GenericArguments.Length) + Type[] requestGenericArguments = request.GenericArguments?.ToArray() ?? Array.Empty(); + if (genericArguments.Length != requestGenericArguments.Length) { throw new IpcFaultException(IpcStatus.BadRequest, $"Generic arguments mismatch."); @@ -193,14 +187,14 @@ private async Task GetReponseAsync(IpcRequest request, IServiceScop object[] args = new object[paramInfos.Length]; for (int i = 0; i < args.Length; i++) { - object origValue = request.Parameters[i]; + object origValue = requestParameters[i]; Type destType = paramInfos[i].ParameterType; if (destType.IsGenericParameter) { - destType = request.GenericArguments[destType.GenericParameterPosition]; + destType = requestGenericArguments[destType.GenericParameterPosition]; } - if (_valueConverter.TryConvert(origValue, destType, out object arg)) + if (_options.ValueConverter.TryConvert(origValue, destType, out object arg)) { args[i] = arg; } @@ -213,7 +207,7 @@ private async Task GetReponseAsync(IpcRequest request, IServiceScop if (method.IsGenericMethod) { - method = method.MakeGenericMethod(request.GenericArguments); + method = method.MakeGenericMethod(requestGenericArguments); } object @return = method.Invoke(service, args); @@ -233,33 +227,42 @@ private async Task GetReponseAsync(IpcRequest request, IServiceScop private static MethodInfo GetUnambiguousMethod(IpcRequest request, object service) { - if (request == null || service == null) + if (request is null) { - return null; + throw new ArgumentNullException(nameof(request)); + } + + if (service is null) + { + throw new ArgumentNullException(nameof(service)); } MethodInfo method = null; // disambiguate - can't just call as before with generics - MethodInfo method = service.GetType().GetMethod(request.MethodName); Type[] types = service.GetType().GetInterfaces(); - System.Collections.Generic.IEnumerable allMethods = types.SelectMany(t => t.GetMethods()); + IEnumerable allMethods = types.SelectMany(t => t.GetMethods()); var serviceMethods = allMethods.Where(t => t.Name == request.MethodName).ToList(); + object[] requestParameters = request.Parameters?.ToArray() ?? Array.Empty(); + Type[] requestGenericArguments = request.GenericArguments?.ToArray() ?? Array.Empty(); + Type[] requestParameterTypes = request.ParameterTypes?.ToArray() ?? Array.Empty(); + foreach (MethodInfo serviceMethod in serviceMethods) { ParameterInfo[] serviceMethodParameters = serviceMethod.GetParameters(); int parameterTypeMatches = 0; - if (serviceMethodParameters.Length == request.Parameters.Length && serviceMethod.GetGenericArguments().Length == request.GenericArguments.Length) + if (serviceMethodParameters.Length == requestParameters.Length && serviceMethod.GetGenericArguments().Length == requestGenericArguments.Length) { for (int parameterIndex = 0; parameterIndex < serviceMethodParameters.Length; parameterIndex++) { Type serviceParameterType = serviceMethodParameters[parameterIndex].ParameterType.IsGenericParameter ? - request.GenericArguments[serviceMethodParameters[parameterIndex].ParameterType.GenericParameterPosition] : + requestGenericArguments[serviceMethodParameters[parameterIndex].ParameterType.GenericParameterPosition] : serviceMethodParameters[parameterIndex].ParameterType; - if (serviceParameterType == request.ParameterTypes[parameterIndex]) + if (serviceParameterType == requestParameterTypes[parameterIndex]) { parameterTypeMatches++; } diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcEndpointOptions.cs b/src/JKang.IpcServiceFramework.Hosting/IpcEndpointOptions.cs new file mode 100644 index 0000000..c08fa1c --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting/IpcEndpointOptions.cs @@ -0,0 +1,19 @@ +using JKang.IpcServiceFramework.Services; +using System; +using System.IO; + +namespace JKang.IpcServiceFramework.Hosting +{ + public class IpcEndpointOptions + { + public int MaxConcurrentCalls { get; set; } = 4; + + public bool IncludeFailureDetailsInResponse { get; set; } + + public Func StreamTranslator { get; set; } + + public IIpcMessageSerializer Serializer { get; set; } = new DefaultIpcMessageSerializer(); + + public IValueConverter ValueConverter { get; set; } = new DefaultValueConverter(); + } +} diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs b/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs index 97b51d8..3a3b898 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcHostBuilder.cs @@ -15,7 +15,6 @@ public IpcHostBuilder(IHostBuilder hostBuilder) _hostBuilder.ConfigureServices((_, services) => { services - .TryAddIpcInternalServices() .AddHostedService(); }); } diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs index d144ace..6d99b6e 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ContractTest.cs @@ -28,9 +28,9 @@ public ContractTest(IpcApplicationFactory factory) { hostBuilder.AddNamedPipeEndpoint(pipeName); }) - .CreateClient(services => + .CreateClient((name, services) => { - services.AddNamedPipeIpcClient(pipeName); + services.AddNamedPipeIpcClient(name, pipeName); }); } diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs index 49fe1be..9c5e8f8 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/EdgeCaseTest.cs @@ -25,9 +25,9 @@ public async Task ServerIsOff_Timeout() { int timeout = 1000; // 1s IIpcClient client = _factory - .CreateClient(services => + .CreateClient((name, services) => { - services.AddNamedPipeIpcClient(options => + services.AddNamedPipeIpcClient(name, (_, options) => { options.PipeName = "inexisted-pipe"; options.ConnectionTimeout = timeout; @@ -47,9 +47,9 @@ await Assert.ThrowsAsync(async () => public void ConnectionCancelled_Throw() { IIpcClient client = _factory - .CreateClient(services => + .CreateClient((name, services) => { - services.AddNamedPipeIpcClient(options => + services.AddNamedPipeIpcClient(name, (_, options) => { options.PipeName = "inexisted-pipe"; options.ConnectionTimeout = Timeout.Infinite; diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs index 52853cd..aaf5b2b 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/ErrorTest.cs @@ -39,9 +39,9 @@ public async Task Exception_ThrowWithoutDetails(string pipeName, string details) options.IncludeFailureDetailsInResponse = false; }); }) - .CreateClient(services => + .CreateClient((name, services) => { - services.AddNamedPipeIpcClient(options => + services.AddNamedPipeIpcClient(name, (_, options) => { options.PipeName = pipeName; }); @@ -70,9 +70,9 @@ public async Task Exception_ThrowWithDetails(string pipeName, string details) options.IncludeFailureDetailsInResponse = true; }); }) - .CreateClient(services => + .CreateClient((name, services) => { - services.AddNamedPipeIpcClient(options => + services.AddNamedPipeIpcClient(name, (_, options) => { options.PipeName = pipeName; }); @@ -96,9 +96,9 @@ public async Task UnserializableInput_ThrowSerializationException(string pipeNam { hostBuilder.AddNamedPipeEndpoint(pipeName); }) - .CreateClient(services => + .CreateClient((name, services) => { - services.AddNamedPipeIpcClient(pipeName); + services.AddNamedPipeIpcClient(name, pipeName); }); await Assert.ThrowsAnyAsync(async () => @@ -119,9 +119,9 @@ public async Task UnserializableOutput_ThrowFaultException(string pipeName) { hostBuilder.AddNamedPipeEndpoint(pipeName); }) - .CreateClient(services => + .CreateClient((name, services) => { - services.AddNamedPipeIpcClient(pipeName); + services.AddNamedPipeIpcClient(name, pipeName); }); IpcFaultException exception = await Assert.ThrowsAnyAsync(async () => diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs index b4ab2b4..cec0653 100644 --- a/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/StreamTranslatorTest.cs @@ -38,9 +38,9 @@ public async Task StreamTranslator_HappyPath(string pipeName, string input, stri options.StreamTranslator = x => new XorStream(x); }); }) - .CreateClient(services => + .CreateClient((name, services) => { - services.AddNamedPipeIpcClient(options => + services.AddNamedPipeIpcClient(name, (_, options) => { options.PipeName = pipeName; options.StreamTranslator = x => new XorStream(x); diff --git a/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs b/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs index 8b07b03..88a9c70 100644 --- a/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs +++ b/src/JKang.IpcServiceFramework.TcpTests/EdgeCaseTest.cs @@ -1,5 +1,6 @@ using JKang.IpcServiceFramework.Client; using JKang.IpcServiceFramework.TcpTests.Fixtures; +using JKang.IpcServiceFramework.Testing; using Microsoft.Extensions.DependencyInjection; using System; using System.Diagnostics; @@ -10,21 +11,29 @@ namespace JKang.IpcServiceFramework.TcpTests { - public class EdgeCaseTest + public class EdgeCaseTest : IClassFixture> { + private readonly IpcApplicationFactory _factory; + + public EdgeCaseTest(IpcApplicationFactory factory) + { + _factory = factory; + } + [Fact] public async Task ConnectionTimeout_Throw() { int timeout = 3000; // 3s - IIpcClient client = new ServiceCollection() - .AddTcpIpcClient(options => + IIpcClient client = _factory + .CreateClient((name, services) => { - // Connect to a non-routable IP address can trigger timeout - options.ServerIp = IPAddress.Parse("10.0.0.0"); - options.ConnectionTimeout = timeout; - }) - .BuildServiceProvider() - .GetRequiredService>(); + services.AddTcpIpcClient(name, (_, options) => + { + // Connect to a non-routable IP address can trigger timeout + options.ServerIp = IPAddress.Parse("10.0.0.0"); + options.ConnectionTimeout = timeout; + }); + }); var sw = Stopwatch.StartNew(); await Assert.ThrowsAsync(async () => @@ -38,14 +47,15 @@ await Assert.ThrowsAsync(async () => [Fact] public void ConnectionCancelled_Throw() { - IIpcClient client = new ServiceCollection() - .AddTcpIpcClient(options => + IIpcClient client = _factory + .CreateClient((name, services) => { - // Connect to a non-routable IP address can trigger timeout - options.ServerIp = IPAddress.Parse("10.0.0.0"); - }) - .BuildServiceProvider() - .GetRequiredService>(); + services.AddTcpIpcClient(name, (_, options) => + { + // Connect to a non-routable IP address can trigger timeout + options.ServerIp = IPAddress.Parse("10.0.0.0"); + }); + }); using var cts = new CancellationTokenSource(); diff --git a/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs b/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs index daa7dc8..11ff614 100644 --- a/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs +++ b/src/JKang.IpcServiceFramework.TcpTests/HappyPathTest.cs @@ -27,9 +27,9 @@ public HappyPathTest(IpcApplicationFactory factory) { hostBuilder.AddTcpEndpoint(IPAddress.Loopback, port); }) - .CreateClient(services => + .CreateClient((name, services) => { - services.AddTcpIpcClient(IPAddress.Loopback, port); + services.AddTcpIpcClient(name, IPAddress.Loopback, port); }); } diff --git a/src/JKang.IpcServiceFramework.Testing/IpcApplicationFactory.cs b/src/JKang.IpcServiceFramework.Testing/IpcApplicationFactory.cs index 6460f9b..6a31135 100644 --- a/src/JKang.IpcServiceFramework.Testing/IpcApplicationFactory.cs +++ b/src/JKang.IpcServiceFramework.Testing/IpcApplicationFactory.cs @@ -34,7 +34,7 @@ public IpcApplicationFactory WithIpcHostConfiguration(Action CreateClient(Action clientConfig) + public IIpcClient CreateClient(Action clientConfig) { if (clientConfig is null) { @@ -48,11 +48,13 @@ public IIpcClient CreateClient(Action clientConfi _host.StartAsync().Wait(); + string clientName = Guid.NewGuid().ToString(); var services = new ServiceCollection(); - clientConfig.Invoke(services); + clientConfig.Invoke(clientName, services); return services.BuildServiceProvider() - .GetRequiredService>(); + .GetRequiredService>() + .CreateClient(clientName); } public void Dispose() From a4c76812a3d3658b33cb91ec3ae67dda9e652220 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 3 May 2020 20:01:16 +0200 Subject: [PATCH 36/53] Refactor/misc (#114) * refactor: remove low value abstractions assemblies * refactor: remove low value abstraction assembly Co-authored-by: Jacques Kang --- src/IpcServiceFramework.sln | 18 ----------------- .../GlobalSuppressions.cs | 10 ---------- ...ng.IpcServiceFramework.Abstractions.csproj | 16 --------------- ...erviceFramework.Client.Abstractions.csproj | 20 ------------------- .../IIpcClient.cs | 0 .../IIpcClientFactory.cs | 0 .../JKang.IpcServiceFramework.Client.csproj | 1 - .../IpcCommunicationException.cs | 0 .../IpcException.cs | 0 .../IpcFaultException.cs | 0 .../IpcRequest.cs | 0 .../IpcResponse.cs | 0 .../IpcSerializationException.cs | 0 .../IpcStatus.cs | 0 .../JKang.IpcServiceFramework.Core.csproj | 4 ---- .../Services/IIpcMessageSerializer.cs | 0 .../Services/IValueConverter.cs | 0 ...rviceFramework.Hosting.Abstractions.csproj | 20 ------------------- .../IIpcEndpoint.cs | 0 .../IIpcHostBuilder.cs | 0 .../IpcHostingConfigurationException.cs | 0 .../JKang.IpcServiceFramework.Hosting.csproj | 1 - .../JKang.IpcServiceFramework.Testing.csproj | 2 +- 23 files changed, 1 insertion(+), 91 deletions(-) delete mode 100644 src/JKang.IpcServiceFramework.Abstractions/GlobalSuppressions.cs delete mode 100644 src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj delete mode 100644 src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj rename src/{JKang.IpcServiceFramework.Client.Abstractions => JKang.IpcServiceFramework.Client}/IIpcClient.cs (100%) rename src/{JKang.IpcServiceFramework.Client.Abstractions => JKang.IpcServiceFramework.Client}/IIpcClientFactory.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/IpcCommunicationException.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/IpcException.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/IpcFaultException.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/IpcRequest.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/IpcResponse.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/IpcSerializationException.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/IpcStatus.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/Services/IIpcMessageSerializer.cs (100%) rename src/{JKang.IpcServiceFramework.Abstractions => JKang.IpcServiceFramework.Core}/Services/IValueConverter.cs (100%) delete mode 100644 src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj rename src/{JKang.IpcServiceFramework.Hosting.Abstractions => JKang.IpcServiceFramework.Hosting}/IIpcEndpoint.cs (100%) rename src/{JKang.IpcServiceFramework.Hosting.Abstractions => JKang.IpcServiceFramework.Hosting}/IIpcHostBuilder.cs (100%) rename src/{JKang.IpcServiceFramework.Hosting.Abstractions => JKang.IpcServiceFramework.Hosting}/IpcHostingConfigurationException.cs (100%) diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index c2125b0..3dbbd0b 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -14,8 +14,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Core.Tests", "JKang.IpcServiceFramework.Core.Tests\JKang.IpcServiceFramework.Core.Tests.csproj", "{1EC81913-883B-487C-A3FD-98A80EDE3225}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Hosting.Abstractions", "JKang.IpcServiceFramework.Hosting.Abstractions\JKang.IpcServiceFramework.Hosting.Abstractions.csproj", "{85324785-234E-4D25-806D-BE7B69800CFD}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Hosting.NamedPipe", "JKang.IpcServiceFramework.Hosting.NamedPipe\JKang.IpcServiceFramework.Hosting.NamedPipe.csproj", "{42A743C1-2E99-4D3B-BA77-ACDF8309DC74}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NamedPipe", "NamedPipe", "{7ED117EC-C390-4D2E-94D3-C00010B63535}" @@ -28,16 +26,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.H EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client.Tcp", "JKang.IpcServiceFramework.Client.Tcp\JKang.IpcServiceFramework.Client.Tcp.csproj", "{E1EE30C2-EF31-47C9-B9FC-D55A94911C40}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Abstractions", "JKang.IpcServiceFramework.Abstractions\JKang.IpcServiceFramework.Abstractions.csproj", "{F8E7A89B-F863-42FF-9FE0-77A107938454}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client", "JKang.IpcServiceFramework.Client\JKang.IpcServiceFramework.Client.csproj", "{46465A56-CF19-4403-A78C-9871BFD8B49E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Core", "JKang.IpcServiceFramework.Core\JKang.IpcServiceFramework.Core.csproj", "{0713BDCA-E193-4333-988E-B8EE196BA0E6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Hosting", "JKang.IpcServiceFramework.Hosting\JKang.IpcServiceFramework.Hosting.csproj", "{5C70C051-D841-4F4C-8C11-E34B74674187}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Client.Abstractions", "JKang.IpcServiceFramework.Client.Abstractions\JKang.IpcServiceFramework.Client.Abstractions.csproj", "{AE3DE1F9-EAF2-4785-9A23-E03F40D52156}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.Testing", "JKang.IpcServiceFramework.Testing\JKang.IpcServiceFramework.Testing.csproj", "{915000BD-9D84-4554-9A91-428923060EC5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JKang.IpcServiceFramework.TcpTests", "JKang.IpcServiceFramework.TcpTests\JKang.IpcServiceFramework.TcpTests.csproj", "{C6625102-AC1B-4D86-8BF8-B2096F701AC2}" @@ -54,10 +48,6 @@ Global {1EC81913-883B-487C-A3FD-98A80EDE3225}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EC81913-883B-487C-A3FD-98A80EDE3225}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EC81913-883B-487C-A3FD-98A80EDE3225}.Release|Any CPU.Build.0 = Release|Any CPU - {85324785-234E-4D25-806D-BE7B69800CFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85324785-234E-4D25-806D-BE7B69800CFD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85324785-234E-4D25-806D-BE7B69800CFD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85324785-234E-4D25-806D-BE7B69800CFD}.Release|Any CPU.Build.0 = Release|Any CPU {42A743C1-2E99-4D3B-BA77-ACDF8309DC74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {42A743C1-2E99-4D3B-BA77-ACDF8309DC74}.Debug|Any CPU.Build.0 = Debug|Any CPU {42A743C1-2E99-4D3B-BA77-ACDF8309DC74}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -74,10 +64,6 @@ Global {E1EE30C2-EF31-47C9-B9FC-D55A94911C40}.Debug|Any CPU.Build.0 = Debug|Any CPU {E1EE30C2-EF31-47C9-B9FC-D55A94911C40}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1EE30C2-EF31-47C9-B9FC-D55A94911C40}.Release|Any CPU.Build.0 = Release|Any CPU - {F8E7A89B-F863-42FF-9FE0-77A107938454}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8E7A89B-F863-42FF-9FE0-77A107938454}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8E7A89B-F863-42FF-9FE0-77A107938454}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8E7A89B-F863-42FF-9FE0-77A107938454}.Release|Any CPU.Build.0 = Release|Any CPU {46465A56-CF19-4403-A78C-9871BFD8B49E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {46465A56-CF19-4403-A78C-9871BFD8B49E}.Debug|Any CPU.Build.0 = Debug|Any CPU {46465A56-CF19-4403-A78C-9871BFD8B49E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -90,10 +76,6 @@ Global {5C70C051-D841-4F4C-8C11-E34B74674187}.Debug|Any CPU.Build.0 = Debug|Any CPU {5C70C051-D841-4F4C-8C11-E34B74674187}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C70C051-D841-4F4C-8C11-E34B74674187}.Release|Any CPU.Build.0 = Release|Any CPU - {AE3DE1F9-EAF2-4785-9A23-E03F40D52156}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AE3DE1F9-EAF2-4785-9A23-E03F40D52156}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AE3DE1F9-EAF2-4785-9A23-E03F40D52156}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AE3DE1F9-EAF2-4785-9A23-E03F40D52156}.Release|Any CPU.Build.0 = Release|Any CPU {915000BD-9D84-4554-9A91-428923060EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {915000BD-9D84-4554-9A91-428923060EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU {915000BD-9D84-4554-9A91-428923060EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/JKang.IpcServiceFramework.Abstractions/GlobalSuppressions.cs b/src/JKang.IpcServiceFramework.Abstractions/GlobalSuppressions.cs deleted file mode 100644 index 3736399..0000000 --- a/src/JKang.IpcServiceFramework.Abstractions/GlobalSuppressions.cs +++ /dev/null @@ -1,10 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Globalization", - "CA1303:Do not pass literals as localized parameters", - Justification = "")] diff --git a/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj b/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj deleted file mode 100644 index bebff63..0000000 --- a/src/JKang.IpcServiceFramework.Abstractions/JKang.IpcServiceFramework.Abstractions.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netstandard2.0 - JKang.IpcServiceFramework - ipc,interprocess,communication,wcf - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj b/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj deleted file mode 100644 index 510db61..0000000 --- a/src/JKang.IpcServiceFramework.Client.Abstractions/JKang.IpcServiceFramework.Client.Abstractions.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netstandard2.0 - JKang.IpcServiceFramework.Client - ipc,interprocess,communication,wcf,client - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs b/src/JKang.IpcServiceFramework.Client/IIpcClient.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClient.cs rename to src/JKang.IpcServiceFramework.Client/IIpcClient.cs diff --git a/src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClientFactory.cs b/src/JKang.IpcServiceFramework.Client/IIpcClientFactory.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Client.Abstractions/IIpcClientFactory.cs rename to src/JKang.IpcServiceFramework.Client/IIpcClientFactory.cs diff --git a/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj b/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj index 414148f..a248d56 100644 --- a/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj +++ b/src/JKang.IpcServiceFramework.Client/JKang.IpcServiceFramework.Client.csproj @@ -10,7 +10,6 @@ - diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcCommunicationException.cs b/src/JKang.IpcServiceFramework.Core/IpcCommunicationException.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/IpcCommunicationException.cs rename to src/JKang.IpcServiceFramework.Core/IpcCommunicationException.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcException.cs b/src/JKang.IpcServiceFramework.Core/IpcException.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/IpcException.cs rename to src/JKang.IpcServiceFramework.Core/IpcException.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcFaultException.cs b/src/JKang.IpcServiceFramework.Core/IpcFaultException.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/IpcFaultException.cs rename to src/JKang.IpcServiceFramework.Core/IpcFaultException.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs b/src/JKang.IpcServiceFramework.Core/IpcRequest.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/IpcRequest.cs rename to src/JKang.IpcServiceFramework.Core/IpcRequest.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs b/src/JKang.IpcServiceFramework.Core/IpcResponse.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/IpcResponse.cs rename to src/JKang.IpcServiceFramework.Core/IpcResponse.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcSerializationException.cs b/src/JKang.IpcServiceFramework.Core/IpcSerializationException.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/IpcSerializationException.cs rename to src/JKang.IpcServiceFramework.Core/IpcSerializationException.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/IpcStatus.cs b/src/JKang.IpcServiceFramework.Core/IpcStatus.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/IpcStatus.cs rename to src/JKang.IpcServiceFramework.Core/IpcStatus.cs diff --git a/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj b/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj index 205ae55..c8a702a 100644 --- a/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj +++ b/src/JKang.IpcServiceFramework.Core/JKang.IpcServiceFramework.Core.csproj @@ -15,8 +15,4 @@ - - - - diff --git a/src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs b/src/JKang.IpcServiceFramework.Core/Services/IIpcMessageSerializer.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/Services/IIpcMessageSerializer.cs rename to src/JKang.IpcServiceFramework.Core/Services/IIpcMessageSerializer.cs diff --git a/src/JKang.IpcServiceFramework.Abstractions/Services/IValueConverter.cs b/src/JKang.IpcServiceFramework.Core/Services/IValueConverter.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Abstractions/Services/IValueConverter.cs rename to src/JKang.IpcServiceFramework.Core/Services/IValueConverter.cs diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj b/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj deleted file mode 100644 index 3bf5359..0000000 --- a/src/JKang.IpcServiceFramework.Hosting.Abstractions/JKang.IpcServiceFramework.Hosting.Abstractions.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netstandard2.0 - JKang.IpcServiceFramework.Hosting - ipc,interprocess,communication,wcf,hosting - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting/IIpcEndpoint.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcEndpoint.cs rename to src/JKang.IpcServiceFramework.Hosting/IIpcEndpoint.cs diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcHostBuilder.cs b/src/JKang.IpcServiceFramework.Hosting/IIpcHostBuilder.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Hosting.Abstractions/IIpcHostBuilder.cs rename to src/JKang.IpcServiceFramework.Hosting/IIpcHostBuilder.cs diff --git a/src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcHostingConfigurationException.cs b/src/JKang.IpcServiceFramework.Hosting/IpcHostingConfigurationException.cs similarity index 100% rename from src/JKang.IpcServiceFramework.Hosting.Abstractions/IpcHostingConfigurationException.cs rename to src/JKang.IpcServiceFramework.Hosting/IpcHostingConfigurationException.cs diff --git a/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj b/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj index 0684922..d83cf20 100644 --- a/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj +++ b/src/JKang.IpcServiceFramework.Hosting/JKang.IpcServiceFramework.Hosting.csproj @@ -15,7 +15,6 @@ - diff --git a/src/JKang.IpcServiceFramework.Testing/JKang.IpcServiceFramework.Testing.csproj b/src/JKang.IpcServiceFramework.Testing/JKang.IpcServiceFramework.Testing.csproj index 3c197f3..61e0bca 100644 --- a/src/JKang.IpcServiceFramework.Testing/JKang.IpcServiceFramework.Testing.csproj +++ b/src/JKang.IpcServiceFramework.Testing/JKang.IpcServiceFramework.Testing.csproj @@ -13,7 +13,7 @@ - + From 018484d36615124cc04e81316dddf35422aadec2 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Sun, 3 May 2020 21:54:49 +0200 Subject: [PATCH 37/53] refactor: update sample project with readme (#115) Co-authored-by: Jacques Kang --- README.md | 15 +++++++++++---- .../IpcServiceSample.ConsoleClient.csproj | 2 +- samples/IpcServiceSample.Client/Program.cs | 16 ++++++++++++---- .../IpcServiceSample.Server.csproj | 2 +- samples/IpcServiceSample.sln | 5 +++++ 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 54613d4..11eeee9 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,17 @@ Named pipeline and TCP support out-of-the-box, extensible with other protocols. 1. Invoke the server ```csharp - IIpcClient client = new ServiceCollection() - .AddNamedPipeIpcClient("my-pipe") - .BuildServiceProvider() - .GetRequiredService>(); + // register IPC clients + ServiceProvider serviceProvider = new ServiceCollection() + .AddNamedPipeIpcClient("client1", pipeName: "pipeinternal") + .BuildServiceProvider(); + + // resolve IPC client factory + IIpcClientFactory clientFactory = serviceProvider + .GetRequiredService>(); + + // create client + IIpcClient client = clientFactory.CreateClient("client1"); string output = await client.InvokeAsync(x => x.ReverseString(input)); ``` diff --git a/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj b/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj index e066bbc..b399a9d 100644 --- a/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj +++ b/samples/IpcServiceSample.Client/IpcServiceSample.ConsoleClient.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/IpcServiceSample.Client/Program.cs b/samples/IpcServiceSample.Client/Program.cs index f51da1f..6df6be4 100644 --- a/samples/IpcServiceSample.Client/Program.cs +++ b/samples/IpcServiceSample.Client/Program.cs @@ -15,10 +15,18 @@ private static async Task Main(string[] args) Console.WriteLine("Type a phrase and press enter or press Ctrl+C to exit:"); string input = Console.ReadLine(); - IIpcClient client = new ServiceCollection() - .AddNamedPipeIpcClient("pipeinternal") - .BuildServiceProvider() - .GetRequiredService>(); + // register IPC clients + ServiceProvider serviceProvider = new ServiceCollection() + .AddNamedPipeIpcClient("client1", pipeName: "pipeinternal") + .BuildServiceProvider(); + + // resolve IPC client factory + IIpcClientFactory clientFactory = serviceProvider + .GetRequiredService>(); + + // create client + IIpcClient client = clientFactory.CreateClient("client1"); + string output = await client.InvokeAsync(x => x.ReverseString(input)); Console.WriteLine($"Result from server: '{output}'.\n"); diff --git a/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj b/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj index 51ba322..102fa7a 100644 --- a/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj +++ b/samples/IpcServiceSample.Server/IpcServiceSample.Server.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/IpcServiceSample.sln b/samples/IpcServiceSample.sln index 70784c8..e84e9e2 100644 --- a/samples/IpcServiceSample.sln +++ b/samples/IpcServiceSample.sln @@ -9,6 +9,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.ConsoleCli EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcServiceSample.Server", "IpcServiceSample.Server\IpcServiceSample.Server.csproj", "{39CFB883-D3E3-43D0-A94C-4F587C45294B}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4F70218C-A4A5-4996-9633-5E0361337DD6}" + ProjectSection(SolutionItems) = preProject + ..\README.md = ..\README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 2074389a635e972aa6974688f55fb31c799bdcef Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Tue, 26 May 2020 11:43:38 +0200 Subject: [PATCH 38/53] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..3602b6c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: jacqueskang +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From c3bf3ad7aace31528dbf4f3e13201e203eb15971 Mon Sep 17 00:00:00 2001 From: brightness007 Date: Fri, 12 Jun 2020 16:58:48 +0800 Subject: [PATCH 39/53] Create NamedPipe with Everyone Access on Windows Platform (#93) * Create NamedPipe with Everyone Access on Windows Platform * Set NamedPipe permissions to Everyone on Windows. Co-authored-by: Yi Ding <3415065+VaslD@users.noreply.github.com> --- ...cServiceFramework.Hosting.NamedPipe.csproj | 1 + .../NamedPipeIpcEndpoint.cs | 33 +++- .../NamedPipeNative.cs | 156 ++++++++++++++++++ 3 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeNative.cs diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj index 85172f4..cef12fb 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/JKang.IpcServiceFramework.Hosting.NamedPipe.csproj @@ -11,6 +11,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs index fd273c2..cdc0e1c 100644 --- a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeIpcEndpoint.cs @@ -1,10 +1,12 @@ -using Microsoft.Extensions.Logging; -using System; +using System; using System.IO; using System.IO.Pipes; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + namespace JKang.IpcServiceFramework.Hosting.NamedPipe { public class NamedPipeIpcEndpoint : IpcEndpoint @@ -30,11 +32,30 @@ protected override async Task WaitAndProcessAsync( throw new ArgumentNullException(nameof(process)); } - using (var server = new NamedPipeServerStream(_options.PipeName, PipeDirection.InOut, _options.MaxConcurrentCalls, - PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) + // https://github.com/PowerShell/PowerShellEditorServices/blob/f45c6312a859cde4aa25ea347a345e1d35238350/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeServerListener.cs#L38-L67 + // Unfortunately, .NET Core does not support passing in a PipeSecurity object into the constructor for + // NamedPipeServerStream so we are creating native Named Pipes and securing them using native APIs. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + PipeSecurity pipeSecurity = new PipeSecurity(); + PipeAccessRule psRule = new PipeAccessRule(@"Everyone", PipeAccessRights.FullControl, System.Security.AccessControl.AccessControlType.Allow); + pipeSecurity.AddAccessRule(psRule); + using (var server = NamedPipeNative.CreateNamedPipe(_options.PipeName, (uint) _options.MaxConcurrentCalls, pipeSecurity)) + { + await server.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false); + await process(server, cancellationToken).ConfigureAwait(false); + } + } + + // Use original logic on other platforms. + else { - await server.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false); - await process(server, cancellationToken).ConfigureAwait(false); + using (var server = new NamedPipeServerStream(_options.PipeName, PipeDirection.InOut, _options.MaxConcurrentCalls, + PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) + { + await server.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false); + await process(server, cancellationToken).ConfigureAwait(false); + } } } } diff --git a/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeNative.cs b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeNative.cs new file mode 100644 index 0000000..589ab44 --- /dev/null +++ b/src/JKang.IpcServiceFramework.Hosting.NamedPipe/NamedPipeNative.cs @@ -0,0 +1,156 @@ +using System; +using System.IO.Pipes; +using System.Runtime.InteropServices; +using System.Security.AccessControl; + +using Microsoft.Win32.SafeHandles; + +namespace JKang.IpcServiceFramework.Hosting.NamedPipe +{ + /// + /// Native API for Named Pipes + /// https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs#L124-L256 + /// + internal static class NamedPipeNative + { + #region Pipe constants + + // Pipe open mode + internal const uint PIPE_ACCESS_DUPLEX = 0x00000003; + + // Pipe modes + internal const uint PIPE_TYPE_BYTE = 0x00000000; + internal const uint FILE_FLAG_OVERLAPPED = 0x40000000; + internal const uint FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000; + internal const uint PIPE_READMODE_BYTE = 0x00000000; + + #endregion + + #region Data structures + + [StructLayout(LayoutKind.Sequential)] + internal class SECURITY_ATTRIBUTES + { + /// + /// The size, in bytes, of this structure. Set this value to the size of the SECURITY_ATTRIBUTES structure. + /// + public int NLength; + + /// + /// A pointer to a security descriptor for the object that controls the sharing of it. + /// + public IntPtr LPSecurityDescriptor = IntPtr.Zero; + + /// + /// A Boolean value that specifies whether the returned handle is inherited when a new process is created. + /// + public bool InheritHandle; + + /// + /// Initializes a new instance of the SECURITY_ATTRIBUTES class + /// + public SECURITY_ATTRIBUTES() + { + this.NLength = 12; + } + } + + #endregion + + #region Pipe methods + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern SafePipeHandle CreateNamedPipe( + string lpName, + uint dwOpenMode, + uint dwPipeMode, + uint nMaxInstances, + uint nOutBufferSize, + uint nInBufferSize, + uint nDefaultTimeOut, + SECURITY_ATTRIBUTES securityAttributes); + + internal static SECURITY_ATTRIBUTES GetSecurityAttributes(GCHandle securityDescriptorPinnedHandle, bool inheritHandle = false) + { + SECURITY_ATTRIBUTES securityAttributes = new NamedPipeNative.SECURITY_ATTRIBUTES(); + securityAttributes.InheritHandle = inheritHandle; + securityAttributes.NLength = (int)Marshal.SizeOf(securityAttributes); + securityAttributes.LPSecurityDescriptor = securityDescriptorPinnedHandle.AddrOfPinnedObject(); + return securityAttributes; + } + + /// + /// Helper method to create a PowerShell transport named pipe via native API, along + /// with a returned .Net NamedPipeServerStream object wrapping the named pipe. + /// + /// Named pipe core name. + /// + /// NamedPipeServerStream + internal static NamedPipeServerStream CreateNamedPipe( + string pipeName, + uint maxNumberOfServerInstances, + PipeSecurity pipeSecurity) + + { + string fullPipeName = @"\\.\pipe\" + pipeName; + + CommonSecurityDescriptor securityDesc = new CommonSecurityDescriptor(false, false, pipeSecurity.GetSecurityDescriptorBinaryForm(), 0); + + // Create optional security attributes based on provided PipeSecurity. + NamedPipeNative.SECURITY_ATTRIBUTES securityAttributes = null; + GCHandle? securityDescHandle = null; + if (securityDesc != null) + { + byte[] securityDescBuffer = new byte[securityDesc.BinaryLength]; + securityDesc.GetBinaryForm(securityDescBuffer, 0); + + securityDescHandle = GCHandle.Alloc(securityDescBuffer, GCHandleType.Pinned); + securityAttributes = NamedPipeNative.GetSecurityAttributes(securityDescHandle.Value); + } + + uint openMode = NamedPipeNative.PIPE_ACCESS_DUPLEX | NamedPipeNative.FILE_FLAG_OVERLAPPED; + if (maxNumberOfServerInstances == 1) + { + openMode |= NamedPipeNative.FILE_FLAG_FIRST_PIPE_INSTANCE; + } + + // Create named pipe. + SafePipeHandle pipeHandle = NamedPipeNative.CreateNamedPipe( + fullPipeName, + openMode, + NamedPipeNative.PIPE_TYPE_BYTE | NamedPipeNative.PIPE_READMODE_BYTE, + maxNumberOfServerInstances, + 1, + 1, + 0, + securityAttributes); + + int lastError = Marshal.GetLastWin32Error(); + if (securityDescHandle != null) + { + securityDescHandle.Value.Free(); + } + + if (pipeHandle.IsInvalid) + { + throw new InvalidOperationException(); + } + + // Create the .Net NamedPipeServerStream wrapper. + try + { + return new NamedPipeServerStream( + PipeDirection.InOut, + true, // IsAsync + false, // IsConnected + pipeHandle); + } + catch (Exception) + { + pipeHandle.Dispose(); + throw; + } + } + #endregion + } +} From 371ba76aaf1cd163c1ee49be9cd14507f190d8d8 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 11:45:37 +0200 Subject: [PATCH 40/53] bugfix: only one endpoint is started when multiple ones are configured #118 --- .../IpcBackgroundService.cs | 9 +-- .../Fixtures/ITestService2.cs | 7 +++ .../MultipleEndpointTest.cs | 57 +++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService2.cs create mode 100644 src/JKang.IpcServiceFramework.NamedPipeTests/MultipleEndpointTest.cs diff --git a/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs b/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs index b797cb1..a6e5a76 100644 --- a/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs +++ b/src/JKang.IpcServiceFramework.Hosting/IpcBackgroundService.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -19,13 +20,9 @@ public IpcBackgroundService( _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); } - protected override async Task ExecuteAsync(CancellationToken stoppingToken) + protected override Task ExecuteAsync(CancellationToken stoppingToken) { - foreach (IIpcEndpoint endpoint in _endpoints) - { - await endpoint.ExecuteAsync(stoppingToken).ConfigureAwait(false); - } - _logger.LogInformation("IPC background service started."); + return Task.WhenAll(_endpoints.Select(x => x.ExecuteAsync(stoppingToken))); } public override void Dispose() diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService2.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService2.cs new file mode 100644 index 0000000..a3d55bb --- /dev/null +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/Fixtures/ITestService2.cs @@ -0,0 +1,7 @@ +namespace JKang.IpcServiceFramework.NamedPipeTests.Fixtures +{ + public interface ITestService2 + { + int SomeMethod(); + } +} diff --git a/src/JKang.IpcServiceFramework.NamedPipeTests/MultipleEndpointTest.cs b/src/JKang.IpcServiceFramework.NamedPipeTests/MultipleEndpointTest.cs new file mode 100644 index 0000000..4113861 --- /dev/null +++ b/src/JKang.IpcServiceFramework.NamedPipeTests/MultipleEndpointTest.cs @@ -0,0 +1,57 @@ +using AutoFixture.Xunit2; +using JKang.IpcServiceFramework.Client; +using JKang.IpcServiceFramework.Hosting; +using JKang.IpcServiceFramework.NamedPipeTests.Fixtures; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Moq; +using System.Threading.Tasks; +using Xunit; + +namespace JKang.IpcServiceFramework.NamedPipeTests +{ + public class MultipleEndpointTest + { + [Theory, AutoData] + public async Task MultipleEndpoints( + Mock service1, + Mock service2) + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services + .AddScoped(x => service1.Object) + .AddScoped(x => service2.Object); + }) + .ConfigureIpcHost(builder => + { + builder + .AddNamedPipeEndpoint("pipe1") + .AddNamedPipeEndpoint("pipe2"); + }) + .Build(); + + await host.StartAsync(); + + ServiceProvider clientServiceProvider = new ServiceCollection() + .AddNamedPipeIpcClient("client1", "pipe1") + .AddNamedPipeIpcClient("client2", "pipe2") + .BuildServiceProvider(); + + IIpcClient client1 = clientServiceProvider + .GetRequiredService>() + .CreateClient("client1"); + + await client1.InvokeAsync(x => x.ReturnVoid()); + service1.Verify(x => x.ReturnVoid(), Times.Once); + + IIpcClient client2 = clientServiceProvider + .GetRequiredService>() + .CreateClient("client2"); + await client2.InvokeAsync(x => x.SomeMethod()); + service2.Verify(x => x.SomeMethod(), Times.Once); + + } + } +} From 0c5ae508fbfb1afcad024446b4664907af1eb893 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 12:02:33 +0200 Subject: [PATCH 41/53] ci: add a parameter to ci pipeline that decide build quality --- .../azure-pipelines-ci.yml | 23 +++++++++++-------- .../azure-pipelines-pr.yml | 0 src/IpcServiceFramework.sln | 4 ++-- 3 files changed, 16 insertions(+), 11 deletions(-) rename azure-pipelines-ci.yml => build/azure-pipelines-ci.yml (72%) rename azure-pipelines-pr.yml => build/azure-pipelines-pr.yml (100%) diff --git a/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml similarity index 72% rename from azure-pipelines-ci.yml rename to build/azure-pipelines-ci.yml index 145b195..88cecd7 100644 --- a/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -1,9 +1,18 @@ -# ASP.NET Core (.NET Framework) -# Build and test ASP.NET Core projects targeting the full .NET Framework. -# Add steps that publish symbols, save build artifacts, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core +parameters: +- name: stable + displayName: Is stable + type: boolean + default: false -name: 3.0.0$(Rev:.r) +variables: + buildConfiguration: 'Release' + version: '3.0.0' + ${{ if parameters.stable }}: + nugetVersion: $(version) + ${{ if eq(parameters.stable, false) }}: + nugetVersion: '$(version)-rc.$(Date:yyyyMMdd)$(Rev:.r)' + +name: $(nugetVersion) trigger: branches: @@ -15,10 +24,6 @@ pr: none pool: vmImage: 'ubuntu-16.04' -variables: - buildConfiguration: 'Release' - nugetVersion: '$(Build.BuildNumber)-alpha' - steps: - task: DotNetCoreCLI@2 displayName: Build diff --git a/azure-pipelines-pr.yml b/build/azure-pipelines-pr.yml similarity index 100% rename from azure-pipelines-pr.yml rename to build/azure-pipelines-pr.yml diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index 3dbbd0b..9129993 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -6,8 +6,8 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20913218-C740-42E9-9D17-CAD973B676D0}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - ..\azure-pipelines-ci.yml = ..\azure-pipelines-ci.yml - ..\azure-pipelines-pr.yml = ..\azure-pipelines-pr.yml + ..\build\azure-pipelines-ci.yml = ..\build\azure-pipelines-ci.yml + ..\build\azure-pipelines-pr.yml = ..\build\azure-pipelines-pr.yml Directory.Build.props = Directory.Build.props IpcServiceFramework.snk = IpcServiceFramework.snk EndProjectSection From 938cea00fb70b0052b76ae4e6b75e83a2e45726d Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 12:07:29 +0200 Subject: [PATCH 42/53] Update azure-pipelines-ci.yml --- build/azure-pipelines-ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index 88cecd7..f87dfca 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -6,11 +6,10 @@ parameters: variables: buildConfiguration: 'Release' - version: '3.0.0' ${{ if parameters.stable }}: - nugetVersion: $(version) + nugetVersion: 3.0.0 ${{ if eq(parameters.stable, false) }}: - nugetVersion: '$(version)-rc.$(Date:yyyyMMdd)$(Rev:.r)' + nugetVersion: '3.0.0-rc.$(Date:yyyyMMdd)$(Rev:.r)' name: $(nugetVersion) @@ -52,4 +51,4 @@ steps: inputs: PathtoPublish: '$(Build.ArtifactStagingDirectory)' ArtifactName: 'drop' - publishLocation: 'Container' \ No newline at end of file + publishLocation: 'Container' From a7974be042fc597e28ce4c6f6c1a11ec071d2cfe Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 12:14:22 +0200 Subject: [PATCH 43/53] Update azure-pipelines-ci.yml --- build/azure-pipelines-ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index f87dfca..9342ab8 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -7,11 +7,11 @@ parameters: variables: buildConfiguration: 'Release' ${{ if parameters.stable }}: - nugetVersion: 3.0.0 + suffix: '' ${{ if eq(parameters.stable, false) }}: - nugetVersion: '3.0.0-rc.$(Date:yyyyMMdd)$(Rev:.r)' + suffix: '-preview' -name: $(nugetVersion) +name: 3.0.0$(Rev:.r)$(suffix) trigger: branches: @@ -44,8 +44,7 @@ steps: command: 'pack' packagesToPack: 'src/**/*.csproj' nobuild: true - versioningScheme: 'byEnvVar' - versionEnvVar: 'nugetVersion' + versioningScheme: 'byBuildNumber' - task: PublishBuildArtifacts@1 inputs: From 4217aea746d7f81373cb01d6c308412e0100a37d Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 12:53:22 +0200 Subject: [PATCH 44/53] Update azure-pipelines-ci.yml for Azure Pipelines --- build/azure-pipelines-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index 9342ab8..49434d6 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -44,7 +44,8 @@ steps: command: 'pack' packagesToPack: 'src/**/*.csproj' nobuild: true - versioningScheme: 'byBuildNumber' + versioningScheme: 'byEnvVar' + versionEnvVar: 'Build.BuildNumber' - task: PublishBuildArtifacts@1 inputs: From 58b1323d4b88f03e0b02200d873aaa943a209e5a Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 13:02:24 +0200 Subject: [PATCH 45/53] Update azure-pipelines-ci.yml for Azure Pipelines --- build/azure-pipelines-ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index 49434d6..6823908 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -6,12 +6,11 @@ parameters: variables: buildConfiguration: 'Release' - ${{ if parameters.stable }}: - suffix: '' - ${{ if eq(parameters.stable, false) }}: - suffix: '-preview' -name: 3.0.0$(Rev:.r)$(suffix) +${{ if parameters.stable }}: + name: 3.0.0 +${{ if eq(parameters.stable, false) }}: + name: 3.0.0-preview$(Rev:.r) trigger: branches: From 0725215357d5564cba5b21218c6dd3d91ca6cfee Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 13:04:22 +0200 Subject: [PATCH 46/53] Update azure-pipelines-ci.yml for Azure Pipelines --- build/azure-pipelines-ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index 6823908..5cece5a 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -6,11 +6,12 @@ parameters: variables: buildConfiguration: 'Release' + ${{ if parameters.stable }}: + nugetVersion: 3.0.0 + ${{ if eq(parameters.stable, false) }}: + nugetVersion: 3.0.0-preview.$(Date:yyyyMMdd)$(Rev:.r) -${{ if parameters.stable }}: - name: 3.0.0 -${{ if eq(parameters.stable, false) }}: - name: 3.0.0-preview$(Rev:.r) +name: $(nugetVersion) trigger: branches: From 2f01b0b0d395476cd10f44f91535c3798363a443 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 13:11:01 +0200 Subject: [PATCH 47/53] Update azure-pipelines-ci.yml for Azure Pipelines --- build/azure-pipelines-ci.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index 5cece5a..146ce8b 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -5,13 +5,14 @@ parameters: default: false variables: + nugetVersion: 3.0.0 buildConfiguration: 'Release' ${{ if parameters.stable }}: - nugetVersion: 3.0.0 + suffix: -stable ${{ if eq(parameters.stable, false) }}: - nugetVersion: 3.0.0-preview.$(Date:yyyyMMdd)$(Rev:.r) + suffix: -preview -name: $(nugetVersion) +name: $(nugetVersion)$(suffix).$(Date:yyyyMMdd)$(Rev:.r) trigger: branches: @@ -45,7 +46,10 @@ steps: packagesToPack: 'src/**/*.csproj' nobuild: true versioningScheme: 'byEnvVar' - versionEnvVar: 'Build.BuildNumber' + ${{ if parameters.stable }}: + versionEnvVar: 'nugetVersion' + ${{ if eq(parameters.stable, false) }}: + versionEnvVar: 'Build.BuildNumber' - task: PublishBuildArtifacts@1 inputs: From 22093116e613c979342527a914d557f48101b4b3 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 13:25:11 +0200 Subject: [PATCH 48/53] ci: separate ci and stable build pipeline --- build/azure-pipelines-ci.yml | 17 ++------------ build/azure-pipelines.yml | 44 ++++++++++++++++++++++++++++++++++++ src/IpcServiceFramework.sln | 1 + 3 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 build/azure-pipelines.yml diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index 146ce8b..fd6a3d3 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -1,18 +1,8 @@ -parameters: -- name: stable - displayName: Is stable - type: boolean - default: false - variables: nugetVersion: 3.0.0 buildConfiguration: 'Release' - ${{ if parameters.stable }}: - suffix: -stable - ${{ if eq(parameters.stable, false) }}: - suffix: -preview -name: $(nugetVersion)$(suffix).$(Date:yyyyMMdd)$(Rev:.r) +name: 3.0.0-ci-$(Date:yyyyMMdd)$(Rev:.r) trigger: branches: @@ -46,10 +36,7 @@ steps: packagesToPack: 'src/**/*.csproj' nobuild: true versioningScheme: 'byEnvVar' - ${{ if parameters.stable }}: - versionEnvVar: 'nugetVersion' - ${{ if eq(parameters.stable, false) }}: - versionEnvVar: 'Build.BuildNumber' + versionEnvVar: 'Build.BuildNumber' - task: PublishBuildArtifacts@1 inputs: diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml new file mode 100644 index 0000000..b4b9bbc --- /dev/null +++ b/build/azure-pipelines.yml @@ -0,0 +1,44 @@ +name: 3.0.0 + +trigger: + branches: + include: + - master + +pr: none + +variables: + buildConfiguration: 'Release' + +pool: + vmImage: 'ubuntu-16.04' + +steps: +- task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: 'build' + projects: 'src/*.sln' + arguments: '--configuration $(buildConfiguration)' + +- task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: 'test' + projects: 'src/*Tests/*.csproj' + arguments: '--configuration $(buildConfiguration)' + +- task: DotNetCoreCLI@2 + displayName: Pack + inputs: + command: 'pack' + packagesToPack: 'src/**/*.csproj' + nobuild: true + versioningScheme: 'byEnvVar' + versionEnvVar: 'Build.BuildNumber' + +- task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: 'drop' + publishLocation: 'Container' diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index 9129993..68ad444 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -8,6 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig ..\build\azure-pipelines-ci.yml = ..\build\azure-pipelines-ci.yml ..\build\azure-pipelines-pr.yml = ..\build\azure-pipelines-pr.yml + ..\build\azure-pipelines.yml = ..\build\azure-pipelines.yml Directory.Build.props = Directory.Build.props IpcServiceFramework.snk = IpcServiceFramework.snk EndProjectSection From d1be4dc7e38913241c7f95ce85251a63d74b72d9 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 13:39:51 +0200 Subject: [PATCH 49/53] Update azure-pipelines-ci.yml for Azure Pipelines --- build/azure-pipelines-ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index fd6a3d3..b8aba2b 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -15,6 +15,9 @@ pool: vmImage: 'ubuntu-16.04' steps: +- checkout: self + persistCredentials: true + - task: DotNetCoreCLI@2 displayName: Build inputs: @@ -43,3 +46,10 @@ steps: PathtoPublish: '$(Build.ArtifactStagingDirectory)' ArtifactName: 'drop' publishLocation: 'Container' + displayName: Publish artifacts + +- script: | + git tag -a $(Build.BuildNumber) -m "v$(Build.BuildNumber)" + git push origin $(Build.BuildNumber) + workingDirectory: $(Build.SourcesDirectory) + displayName: Tag source \ No newline at end of file From 7f2561a165763a3237ace5161cbb1ede8d4c7c91 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 13:43:59 +0200 Subject: [PATCH 50/53] Update azure-pipelines-ci.yml for Azure Pipelines --- build/azure-pipelines-ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index b8aba2b..51ca85c 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -49,7 +49,9 @@ steps: displayName: Publish artifacts - script: | - git tag -a $(Build.BuildNumber) -m "v$(Build.BuildNumber)" - git push origin $(Build.BuildNumber) + git config --global user.name "CI Build" + git config --global user.email "fake@dev.azure.com" + git tag -a $(Build.BuildNumber) -m "v$(Build.BuildNumber)" + git push origin $(Build.BuildNumber) workingDirectory: $(Build.SourcesDirectory) displayName: Tag source \ No newline at end of file From 0bd68c336d094d7783882e33349a9634128fd9e4 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 14:18:57 +0200 Subject: [PATCH 51/53] ci: refactor out reusable template (#121) ci: refactor out reusable template --- build/azure-pipelines-ci.yml | 48 ++++-------------------------- build/azure-pipelines-pr.yml | 27 +++++------------ build/azure-pipelines.yml | 51 ++++++++++++-------------------- build/templates/build-test.yml | 19 ++++++++++++ build/templates/pack-publish.yml | 17 +++++++++++ build/version.yml | 2 ++ src/IpcServiceFramework.sln | 3 -- 7 files changed, 70 insertions(+), 97 deletions(-) create mode 100644 build/templates/build-test.yml create mode 100644 build/templates/pack-publish.yml create mode 100644 build/version.yml diff --git a/build/azure-pipelines-ci.yml b/build/azure-pipelines-ci.yml index 51ca85c..393de3e 100644 --- a/build/azure-pipelines-ci.yml +++ b/build/azure-pipelines-ci.yml @@ -1,8 +1,7 @@ variables: - nugetVersion: 3.0.0 - buildConfiguration: 'Release' +- template: version.yml -name: 3.0.0-ci-$(Date:yyyyMMdd)$(Rev:.r) +name: $(version)-ci-$(Date:yyyyMMdd)$(Rev:.r) trigger: branches: @@ -15,43 +14,8 @@ pool: vmImage: 'ubuntu-16.04' steps: -- checkout: self - persistCredentials: true +- template: templates/build-test.yml + parameters: + buildConfiguration: Release -- task: DotNetCoreCLI@2 - displayName: Build - inputs: - command: 'build' - projects: 'src/*.sln' - arguments: '--configuration $(buildConfiguration)' - -- task: DotNetCoreCLI@2 - displayName: Test - inputs: - command: 'test' - projects: 'src/*Tests/*.csproj' - arguments: '--configuration $(buildConfiguration)' - -- task: DotNetCoreCLI@2 - displayName: Pack - inputs: - command: 'pack' - packagesToPack: 'src/**/*.csproj' - nobuild: true - versioningScheme: 'byEnvVar' - versionEnvVar: 'Build.BuildNumber' - -- task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)' - ArtifactName: 'drop' - publishLocation: 'Container' - displayName: Publish artifacts - -- script: | - git config --global user.name "CI Build" - git config --global user.email "fake@dev.azure.com" - git tag -a $(Build.BuildNumber) -m "v$(Build.BuildNumber)" - git push origin $(Build.BuildNumber) - workingDirectory: $(Build.SourcesDirectory) - displayName: Tag source \ No newline at end of file +- template: templates/pack-publish.yml diff --git a/build/azure-pipelines-pr.yml b/build/azure-pipelines-pr.yml index 9635b03..c874066 100644 --- a/build/azure-pipelines-pr.yml +++ b/build/azure-pipelines-pr.yml @@ -1,7 +1,7 @@ -# ASP.NET Core (.NET Framework) -# Build and test ASP.NET Core projects targeting the full .NET Framework. -# Add steps that publish symbols, save build artifacts, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core +variables: +- template: version.yml + +name: $(version)-pr-$(Date:yyyyMMdd)$(Rev:.r) trigger: none @@ -13,20 +13,7 @@ pr: pool: vmImage: 'ubuntu-16.04' -variables: - BuildConfiguration: 'Release' - steps: -- task: DotNetCoreCLI@2 - displayName: Build - inputs: - command: build - projects: '**/*.sln' - arguments: '--configuration $(buildConfiguration)' - -- task: DotNetCoreCLI@2 - displayName: Test - inputs: - command: test - projects: '**/*Tests/*.csproj' - arguments: '--configuration $(buildConfiguration)' +- template: templates/build-test.yml + parameters: + buildConfiguration: Debug diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index b4b9bbc..556261d 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -1,4 +1,7 @@ -name: 3.0.0 +variables: +- template: version.yml + +name: $(version) trigger: branches: @@ -7,38 +10,22 @@ trigger: pr: none -variables: - buildConfiguration: 'Release' - pool: vmImage: 'ubuntu-16.04' steps: -- task: DotNetCoreCLI@2 - displayName: Build - inputs: - command: 'build' - projects: 'src/*.sln' - arguments: '--configuration $(buildConfiguration)' - -- task: DotNetCoreCLI@2 - displayName: Test - inputs: - command: 'test' - projects: 'src/*Tests/*.csproj' - arguments: '--configuration $(buildConfiguration)' - -- task: DotNetCoreCLI@2 - displayName: Pack - inputs: - command: 'pack' - packagesToPack: 'src/**/*.csproj' - nobuild: true - versioningScheme: 'byEnvVar' - versionEnvVar: 'Build.BuildNumber' - -- task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)' - ArtifactName: 'drop' - publishLocation: 'Container' +- checkout: self + persistCredentials: true + +- template: templates/build-test.yml + parameters: + buildConfiguration: Release + +- template: templates/pack-publish.yml + +- script: | + git config --global user.name "Azure DevOps" + git config --global user.email "fake@dev.azure.com" + git tag -a $(Build.BuildNumber) -m "v$(Build.BuildNumber)" + git push origin $(Build.BuildNumber) + displayName: Tag source \ No newline at end of file diff --git a/build/templates/build-test.yml b/build/templates/build-test.yml new file mode 100644 index 0000000..d58b29c --- /dev/null +++ b/build/templates/build-test.yml @@ -0,0 +1,19 @@ +parameters: +- name: buildConfiguration + type: string + default: Release + +steps: +- task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: 'build' + projects: 'src/*.sln' + arguments: '--configuration ${{parameters.buildConfiguration}}' + +- task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: 'test' + projects: 'src/*Tests/*.csproj' + arguments: '--configuration ${{parameters.buildConfiguration}}' diff --git a/build/templates/pack-publish.yml b/build/templates/pack-publish.yml new file mode 100644 index 0000000..3c11474 --- /dev/null +++ b/build/templates/pack-publish.yml @@ -0,0 +1,17 @@ +steps: +- task: DotNetCoreCLI@2 + displayName: Pack + inputs: + command: 'pack' + packagesToPack: 'src/**/*.csproj' + nobuild: true + versioningScheme: 'byEnvVar' + versionEnvVar: 'Build.BuildNumber' + +- task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: 'drop' + publishLocation: 'Container' + displayName: Publish artifacts + diff --git a/build/version.yml b/build/version.yml new file mode 100644 index 0000000..d7e6613 --- /dev/null +++ b/build/version.yml @@ -0,0 +1,2 @@ +variables: + version: '3.0.0' \ No newline at end of file diff --git a/src/IpcServiceFramework.sln b/src/IpcServiceFramework.sln index 68ad444..a9c4a95 100644 --- a/src/IpcServiceFramework.sln +++ b/src/IpcServiceFramework.sln @@ -6,9 +6,6 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20913218-C740-42E9-9D17-CAD973B676D0}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - ..\build\azure-pipelines-ci.yml = ..\build\azure-pipelines-ci.yml - ..\build\azure-pipelines-pr.yml = ..\build\azure-pipelines-pr.yml - ..\build\azure-pipelines.yml = ..\build\azure-pipelines.yml Directory.Build.props = Directory.Build.props IpcServiceFramework.snk = IpcServiceFramework.snk EndProjectSection From d2009e455f9bd998508545239df1799d0c853ffa Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 14:22:17 +0200 Subject: [PATCH 52/53] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 11eeee9..5917dda 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -[![Build Status](https://dev.azure.com/jacques-kang/IpcServiceFramework/_apis/build/status/IpcServiceFramework%20CI?branchName=develop)](https://dev.azure.com/jacques-kang/IpcServiceFramework/_build/latest?definitionId=9&branchName=develop) +| CI build | Stable build | +|----------|--------------| +|[![Build Status](https://dev.azure.com/jacques-kang/IpcServiceFramework/_apis/build/status/IpcServiceFramework%20CI?branchName=develop)](https://dev.azure.com/jacques-kang/IpcServiceFramework/_build/latest?definitionId=9&branchName=develop)|[![Build Status](https://dev.azure.com/jacques-kang/IpcServiceFramework/_apis/build/status/IpcServiceFramework?branchName=master)](https://dev.azure.com/jacques-kang/IpcServiceFramework/_build/latest?definitionId=14&branchName=master)| # IpcServiceFramework From 5d3b25d524e4ebe2799061e314e9f26ee16ec425 Mon Sep 17 00:00:00 2001 From: Jacques Kang Date: Fri, 12 Jun 2020 14:25:32 +0200 Subject: [PATCH 53/53] ci: fix the build --- build/azure-pipelines.yml | 4 ++-- build/templates/pack-publish.yml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 556261d..f586cde 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -26,6 +26,6 @@ steps: - script: | git config --global user.name "Azure DevOps" git config --global user.email "fake@dev.azure.com" - git tag -a $(Build.BuildNumber) -m "v$(Build.BuildNumber)" - git push origin $(Build.BuildNumber) + git tag -a "v$(Build.BuildNumber)" -m "v$(Build.BuildNumber)" + git push origin "v$(Build.BuildNumber)" displayName: Tag source \ No newline at end of file diff --git a/build/templates/pack-publish.yml b/build/templates/pack-publish.yml index 3c11474..26f2de5 100644 --- a/build/templates/pack-publish.yml +++ b/build/templates/pack-publish.yml @@ -4,6 +4,7 @@ steps: inputs: command: 'pack' packagesToPack: 'src/**/*.csproj' + configuration: Release nobuild: true versioningScheme: 'byEnvVar' versionEnvVar: 'Build.BuildNumber'