diff --git a/src/NuGet.Config b/NuGet.Config
similarity index 56%
rename from src/NuGet.Config
rename to NuGet.Config
index ba6d1755..437e25e1 100644
--- a/src/NuGet.Config
+++ b/NuGet.Config
@@ -2,5 +2,6 @@
+
\ No newline at end of file
diff --git a/benchmarks/Benchmarks.sln b/benchmarks/Benchmarks.sln
new file mode 100644
index 00000000..31395937
--- /dev/null
+++ b/benchmarks/Benchmarks.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiPath.CoreIpc", "..\src\UiPath.CoreIpc\UiPath.CoreIpc.csproj", "{6E3048EE-987B-4846-84EF-305A286092B1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiPath.Ipc.Benchmarks", "UiPath.Ipc.Benchmarks\UiPath.Ipc.Benchmarks.csproj", "{D12CCF62-00C5-4291-A6A8-D3321C107EE7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6E3048EE-987B-4846-84EF-305A286092B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6E3048EE-987B-4846-84EF-305A286092B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6E3048EE-987B-4846-84EF-305A286092B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6E3048EE-987B-4846-84EF-305A286092B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D12CCF62-00C5-4291-A6A8-D3321C107EE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D12CCF62-00C5-4291-A6A8-D3321C107EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D12CCF62-00C5-4291-A6A8-D3321C107EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D12CCF62-00C5-4291-A6A8-D3321C107EE7}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/benchmarks/UiPath.Ipc.Benchmarks/IpcBenchmark.cs b/benchmarks/UiPath.Ipc.Benchmarks/IpcBenchmark.cs
new file mode 100644
index 00000000..d1da662d
--- /dev/null
+++ b/benchmarks/UiPath.Ipc.Benchmarks/IpcBenchmark.cs
@@ -0,0 +1,41 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using UiPath.Ipc.Benchmarks;
+
+[SimpleJob(RuntimeMoniker.Net461, baseline: true)]
+[SimpleJob(RuntimeMoniker.Net80)]
+[RPlotExporter]
+public class IpcBenchmark
+{
+ private Technology _technology = null!;
+ private Technology.IProxy _proxy = null!;
+
+ [Params(TechnologyId.Old, TechnologyId.New)]
+ public TechnologyId TechId;
+
+ [GlobalSetup]
+ public async Task Setup()
+ {
+ _technology = TechId.Create();
+ await _technology.Init();
+ _proxy = _technology.GetProxy();
+ }
+
+ [GlobalCleanup]
+ public async Task Cleanup()
+ {
+ await _technology.DisposeAsync();
+ }
+
+ [Benchmark]
+ public async Task SimpleCall()
+ {
+ _ = await _proxy.AddFloats(1, 1);
+ }
+
+ [Benchmark]
+ public async Task CallWithCallback()
+ {
+ _ = await _proxy.GetCallbackThreadName();
+ }
+}
\ No newline at end of file
diff --git a/benchmarks/UiPath.Ipc.Benchmarks/Program.cs b/benchmarks/UiPath.Ipc.Benchmarks/Program.cs
new file mode 100644
index 00000000..9d353e5c
--- /dev/null
+++ b/benchmarks/UiPath.Ipc.Benchmarks/Program.cs
@@ -0,0 +1,15 @@
+using BenchmarkDotNet.Running;
+using UiPath.Ipc.Benchmarks;
+
+
+// await Run(TechnologyId.New);
+// BenchmarkRunner.Run();
+BenchmarkRunner.Run();
+
+static async Task Run(TechnologyId techId)
+{
+ await using var tech = techId.Create();
+ await tech.Init();
+ var x = await tech.GetProxy().AddFloats(1, 1);
+ Console.WriteLine($"{nameof(x)} == {x}");
+}
\ No newline at end of file
diff --git a/benchmarks/UiPath.Ipc.Benchmarks/SchedulerBenchmark.cs b/benchmarks/UiPath.Ipc.Benchmarks/SchedulerBenchmark.cs
new file mode 100644
index 00000000..7b773a86
--- /dev/null
+++ b/benchmarks/UiPath.Ipc.Benchmarks/SchedulerBenchmark.cs
@@ -0,0 +1,65 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using Nito.AsyncEx;
+
+[SimpleJob(RuntimeMoniker.Net461, baseline: true)]
+[SimpleJob(RuntimeMoniker.Net80)]
+[RPlotExporter]
+public class SchedulerBenchmark
+{
+ private TaskScheduler _scheduler = null!;
+
+ [Params(SchedulerKind.ConcurrentExclusive, SchedulerKind.AsyncContextThread)]
+ public SchedulerKind SchedulerKind;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _scheduler = SchedulerKind.Create();
+ }
+
+ [GlobalCleanup]
+ public void Cleanup()
+ {
+ _scheduler = null!;
+ }
+
+ [Benchmark]
+ public void Schedule()
+ {
+ _scheduler.RunAsync(async () =>
+ {
+ });
+ }
+}
+
+public enum SchedulerKind
+{
+ ConcurrentExclusive,
+ AsyncContextThread
+}
+
+public static class SchedulerKindExtensions
+{
+ public static TaskScheduler Create(this SchedulerKind schedulerKind)
+ {
+ switch (schedulerKind)
+ {
+ case SchedulerKind.ConcurrentExclusive:
+ return new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
+ case SchedulerKind.AsyncContextThread:
+ return new AsyncContextThread().Context.Scheduler;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(schedulerKind));
+ }
+ }
+}
+
+public static class TaskExtensions
+{
+ public static Task RunAsync(this TaskScheduler scheduler, Func asyncAction)
+ => Task.Factory.StartNew(asyncAction, CancellationToken.None, TaskCreationOptions.DenyChildAttach, scheduler).Unwrap();
+
+ public static Task RunAsync(this TaskScheduler scheduler, Func> asyncFunc)
+ => Task.Factory.StartNew(asyncFunc, CancellationToken.None, TaskCreationOptions.DenyChildAttach, scheduler).Unwrap();
+}
\ No newline at end of file
diff --git a/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.New.cs b/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.New.cs
new file mode 100644
index 00000000..24fa49cf
--- /dev/null
+++ b/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.New.cs
@@ -0,0 +1,106 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using UiPath.Ipc.Transport.NamedPipe;
+
+namespace UiPath.Ipc.Benchmarks;
+
+partial class Technology
+{
+ public sealed class New : Technology, IProxy
+ {
+ private const string PipeName = "BenchmarkPipeNew";
+ private const int ConcurrentAccepts = 10;
+ private const int MaxRequestMB = 100;
+
+ private static readonly TimeSpan ServerTimeout = TimeSpan.FromDays(1);
+ private static readonly TimeSpan ClientTimeout = TimeSpan.FromDays(1);
+ private readonly TaskScheduler _scheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
+
+ private readonly ComputingCallback _computingCallback = new();
+ private readonly ServiceProvider _serviceProvider;
+ private readonly IpcServer _server;
+ private readonly ClientBase _client;
+ private readonly IComputingService _proxy;
+
+ public New()
+ {
+ _serviceProvider = new ServiceCollection()
+ .AddLogging()
+ .AddSingleton()
+ .BuildServiceProvider();
+
+ _server = new IpcServer
+ {
+ Endpoints = new()
+ {
+ { typeof(IComputingService) }
+ },
+ Listeners = [
+ new NamedPipeListener
+ {
+ PipeName = PipeName,
+ RequestTimeout = ServerTimeout,
+ ConcurrentAccepts = ConcurrentAccepts,
+ MaxReceivedMessageSizeInMegabytes = MaxRequestMB
+ }
+ ],
+ Scheduler = _scheduler,
+ ServiceProvider = _serviceProvider
+ };
+
+ _client = new NamedPipeClient
+ {
+ PipeName = PipeName,
+ AllowImpersonation = true,
+ RequestTimeout = TimeSpan.FromSeconds(5),
+ Scheduler = _scheduler,
+ Callbacks = new()
+ {
+ { typeof(IComputingCallback), _computingCallback }
+ }
+ };
+
+ _proxy = _client.GetProxy();
+ }
+
+ public override async Task Init()
+ {
+ _server.Start();
+ _ = await GetProxy().GetCallbackThreadName();
+ }
+
+ public override async ValueTask DisposeAsync()
+ {
+ (_proxy as IpcProxy)!.Dispose();
+ await _server.DisposeAsync();
+ }
+
+ public override IProxy GetProxy() => this;
+
+ Task IProxy.AddFloats(float x, float y) => _proxy.AddFloats(x, y);
+ Task IProxy.GetCallbackThreadName() => _proxy.GetCallbackThreadName(TimeSpan.Zero);
+
+
+ private interface IComputingService
+ {
+ Task AddFloats(float x, float y, CancellationToken ct = default);
+ Task GetCallbackThreadName(TimeSpan duration, Message message = null!, CancellationToken cancellationToken = default);
+ }
+
+ private sealed class ComputingService(ILogger logger) : IComputingService
+ {
+ public async Task AddFloats(float a, float b, CancellationToken ct = default)
+ {
+ logger.LogInformation($"{nameof(AddFloats)} called.");
+ return a + b;
+ }
+
+ public async Task GetCallbackThreadName(TimeSpan duration, Message message = null!, CancellationToken cancellationToken = default)
+ {
+ await Task.Delay(duration);
+ return await message.GetCallback().GetThreadName();
+ }
+ }
+ }
+}
+
diff --git a/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.Old.cs b/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.Old.cs
new file mode 100644
index 00000000..7e027603
--- /dev/null
+++ b/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.Old.cs
@@ -0,0 +1,99 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using UiPath.CoreIpc;
+using UiPath.CoreIpc.NamedPipe;
+
+namespace UiPath.Ipc.Benchmarks;
+
+partial class Technology
+{
+ public sealed class Old : Technology, IProxy
+ {
+ private const string PipeName = "BenchmarkPipeOld";
+ private const int ConcurrentAccepts = 10;
+ private const int MaxRequestMB = 100;
+
+ private static readonly TimeSpan ServerTimeout = TimeSpan.FromDays(1);
+ private static readonly TimeSpan ClientTimeout = TimeSpan.FromSeconds(1);
+ private readonly TaskScheduler _scheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
+ private Task? _serverRunning;
+
+ private readonly ComputingCallback _computingCallback = new();
+ private readonly ServiceProvider _serverServiceProvider;
+ private readonly ServiceProvider _clientServiceProvider;
+ private readonly ServiceHost _serviceHost;
+ private readonly IComputingService _proxy;
+
+ public Old()
+ {
+ _serverServiceProvider = new ServiceCollection()
+ .AddIpc()
+ .AddLogging()
+ .AddSingleton()
+ .BuildServiceProvider();
+
+ _clientServiceProvider = new ServiceCollection()
+ .AddIpc()
+ .AddLogging()
+ .AddSingleton(_computingCallback)
+ .BuildServiceProvider();
+
+ _serviceHost = new ServiceHostBuilder(_serverServiceProvider)
+ .AddEndpoint()
+ .UseNamedPipes(new NamedPipeSettings(PipeName)
+ {
+ RequestTimeout = ServerTimeout,
+ ConcurrentAccepts = ConcurrentAccepts,
+ MaxReceivedMessageSizeInMegabytes = MaxRequestMB,
+ })
+ .Build();
+
+ _proxy = new NamedPipeClientBuilder(PipeName, _clientServiceProvider)
+ .AllowImpersonation()
+ .RequestTimeout(ClientTimeout)
+ .TaskScheduler(_scheduler)
+ .CallbackInstance(_computingCallback)
+ .Build();
+ }
+
+ public override async Task Init()
+ {
+ _serverRunning = _serviceHost.RunAsync(_scheduler);
+ _ = await GetProxy().GetCallbackThreadName();
+ }
+
+ public override async ValueTask DisposeAsync()
+ {
+ (_proxy as CoreIpc.IpcProxy)!.Dispose();
+ _serviceHost.Dispose();
+ await (_serverRunning ?? Task.CompletedTask);
+ }
+
+ public override IProxy GetProxy() => this;
+
+ Task IProxy.AddFloats(float x, float y) => _proxy.AddFloats(x, y);
+ Task IProxy.GetCallbackThreadName() => _proxy.GetCallbackThreadName(TimeSpan.Zero);
+
+ private interface IComputingService
+ {
+ Task AddFloats(float x, float y, CancellationToken ct = default);
+ Task GetCallbackThreadName(TimeSpan duration, CoreIpc.Message message = null!, CancellationToken cancellationToken = default);
+ }
+
+ private sealed class ComputingService(ILogger logger) : IComputingService
+ {
+ public async Task AddFloats(float a, float b, CancellationToken ct = default)
+ {
+ logger.LogInformation($"{nameof(AddFloats)} called.");
+ return a + b;
+ }
+
+ public async Task GetCallbackThreadName(TimeSpan duration, CoreIpc.Message message = null!, CancellationToken cancellationToken = default)
+ {
+ await Task.Delay(duration);
+ return await message.GetCallback().GetThreadName();
+ }
+ }
+ }
+}
+
diff --git a/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.cs b/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.cs
new file mode 100644
index 00000000..a99db4f6
--- /dev/null
+++ b/benchmarks/UiPath.Ipc.Benchmarks/Switch/Technology.cs
@@ -0,0 +1,26 @@
+namespace UiPath.Ipc.Benchmarks;
+
+public abstract partial class Technology : IAsyncDisposable
+{
+ public abstract Task Init();
+ public abstract ValueTask DisposeAsync();
+
+ public abstract IProxy GetProxy();
+
+ public interface IProxy
+ {
+ Task AddFloats(float x, float y);
+ Task GetCallbackThreadName();
+ }
+
+ public interface IComputingCallback
+ {
+ Task GetThreadName();
+ }
+
+ public sealed class ComputingCallback : IComputingCallback
+ {
+ public async Task GetThreadName() => Thread.CurrentThread.Name!;
+ }
+}
+
diff --git a/benchmarks/UiPath.Ipc.Benchmarks/Switch/TechnologyId.cs b/benchmarks/UiPath.Ipc.Benchmarks/Switch/TechnologyId.cs
new file mode 100644
index 00000000..11172685
--- /dev/null
+++ b/benchmarks/UiPath.Ipc.Benchmarks/Switch/TechnologyId.cs
@@ -0,0 +1,20 @@
+
+namespace UiPath.Ipc.Benchmarks;
+
+public enum TechnologyId
+{
+ Old,
+ New
+}
+
+public static class TechnologyIdExtensions
+{
+ public static Technology Create(this TechnologyId id)
+ => id switch
+ {
+ TechnologyId.Old => new Technology.Old(),
+ TechnologyId.New => new Technology.New(),
+ _ => throw new NotSupportedException()
+ };
+}
+
diff --git a/benchmarks/UiPath.Ipc.Benchmarks/UiPath.Ipc.Benchmarks.csproj b/benchmarks/UiPath.Ipc.Benchmarks/UiPath.Ipc.Benchmarks.csproj
new file mode 100644
index 00000000..471d9092
--- /dev/null
+++ b/benchmarks/UiPath.Ipc.Benchmarks/UiPath.Ipc.Benchmarks.csproj
@@ -0,0 +1,23 @@
+
+
+
+ Exe
+ net8.0;net461
+ enable
+ enable
+ latest
+ 1998
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CI/azp-dotnet-dist.yaml b/src/CI/azp-dotnet-dist.yaml
index bafcc806..02e66263 100644
--- a/src/CI/azp-dotnet-dist.yaml
+++ b/src/CI/azp-dotnet-dist.yaml
@@ -16,7 +16,7 @@ steps:
- task: DotNetCoreCLI@2
displayName: 'dotnet push to UiPath-Internal'
- condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
+ condition: succeeded()
inputs:
command: push
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
@@ -24,7 +24,7 @@ steps:
- task: PublishSymbols@2
displayName: 'Publish Symbols to UiPath Azure Artifacts Symbol Server'
- condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
+ condition: succeeded()
inputs:
symbolsFolder: $(Build.SourcesDirectory)
searchPattern: '**/UiPath.CoreIpc/bin/**/UiPath.CoreIpc.pdb'
diff --git a/src/CI/azp-dotnet.yaml b/src/CI/azp-dotnet.yaml
index 0af4fc84..515a8ea1 100644
--- a/src/CI/azp-dotnet.yaml
+++ b/src/CI/azp-dotnet.yaml
@@ -1,4 +1,10 @@
steps:
+ - task: DotNetCoreCLI@2
+ displayName: '$(Label_DotNet) Restore, build and pack'
+ inputs:
+ projects: '$(DotNet_SessionSolution)'
+ arguments: '--configuration $(DotNet_BuildConfiguration) -p:Version="$(FullVersion)" -p:DefineConstantsEx="CI"'
+
- task: DotNetCoreCLI@2
displayName: '$(Label_DotNet) Run unit tests'
inputs:
diff --git a/src/CI/azp-initialization.yaml b/src/CI/azp-initialization.yaml
index 3cc0d0a3..2fd099a2 100644
--- a/src/CI/azp-initialization.yaml
+++ b/src/CI/azp-initialization.yaml
@@ -1,4 +1,20 @@
steps:
+ - powershell: |
+ Write-Host "##vso[task.setvariable variable=DotnetRuntimeVersion;]8.0.0"
+ Write-Host "##vso[task.setvariable variable=DOTNET_NOLOGO;]true"
+ displayName: 'Use .NET Runtime 8.0.0'
+
+ - task: UseDotNet@2
+ displayName: 'Use .NET SDK 6.0.317'
+ inputs:
+ packageType: 'sdk'
+ version: '6.0.317'
+
+ - task: UseDotNet@2
+ displayName: 'Use .NET SDK 8.x'
+ inputs:
+ packageType: 'sdk'
+ version: 8.x
# Read $(Version) from the UiPath.CoreIpc.csproj file
- powershell: |
diff --git a/src/CI/azp-nodejs.yaml b/src/CI/azp-nodejs.yaml
index 52f7d934..ee14c6bf 100644
--- a/src/CI/azp-nodejs.yaml
+++ b/src/CI/azp-nodejs.yaml
@@ -48,7 +48,7 @@
inputs:
workingDirectory: $(NodeJS_ProjectPath)
script: 'npm test'
-
+
- task: PublishTestResults@2
displayName: 'Publish Web Test Results'
condition: succeededOrFailed()
diff --git a/src/CI/azp-start.yaml b/src/CI/azp-start.yaml
index d659af93..d51e5497 100644
--- a/src/CI/azp-start.yaml
+++ b/src/CI/azp-start.yaml
@@ -10,7 +10,7 @@ variables:
DotNet_MainProjectName: 'UiPath.CoreIpc'
DotNet_MainProjectPath: './src/UiPath.CoreIpc/UiPath.CoreIpc.csproj'
DotNet_ArtifactName: 'NuGet package'
-
+
NodeJS_DotNet_BuildConfiguration: 'Debug'
NodeJS_ProjectPath: './src/Clients/js'
NodeJS_ArchivePath: './src/Clients/js/dist/pack/nodejs.zip'
@@ -23,32 +23,32 @@ stages:
- stage: Build
displayName: '🏭 Build'
jobs:
- # The following 3 jobs will run in parallel:
- - job:
- displayName: '.NET on Windows'
- pool:
+ # The following 3 jobs will run in parallel:
+ - job:
+ displayName: '.NET on Windows'
+ pool:
vmImage: 'windows-2022'
- steps:
- - template: azp-initialization.yaml
- - template: azp-dotnet.yaml
- - template: azp-dotnet-dist.yaml
-
- - job:
- displayName: 'node.js on Windows'
- pool:
+ steps:
+ - template: azp-initialization.yaml
+ - template: azp-dotnet.yaml
+ - template: azp-dotnet-dist.yaml
+
+ - job:
+ displayName: 'node.js on Windows'
+ pool:
vmImage: 'windows-2022'
- steps:
- - template: azp-initialization.yaml
- - template: azp-nodejs.yaml
- - template: azp-nodejs-dist.yaml
-
- - job:
- displayName: 'node.js on Ubuntu'
- pool:
- vmImage: 'ubuntu-20.04'
- steps:
- - template: azp-initialization.yaml
- - template: azp-nodejs.yaml
+ steps:
+ - template: azp-initialization.yaml
+ - template: azp-nodejs.yaml
+ - template: azp-nodejs-dist.yaml
+
+ - job:
+ displayName: 'node.js on Ubuntu'
+ pool:
+ vmImage: 'ubuntu-20.04'
+ steps:
+ - template: azp-initialization.yaml
+ - template: azp-nodejs.yaml
- stage: Publish
displayName: 🚚 Publish
diff --git a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop.sln b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop.sln
index e96e6ecc..13bc8866 100644
--- a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop.sln
+++ b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30320.27
+# Visual Studio Version 17
+VisualStudioVersion = 17.10.35027.167
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiPath.CoreIpc.NodeInterop", "UiPath.CoreIpc.NodeInterop\UiPath.CoreIpc.NodeInterop.csproj", "{B514D2A2-B8ED-4A2A-BDE7-42F74A316FBE}"
EndProject
diff --git a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Contracts.cs b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Contracts.cs
index 0a725bab..d86c0d8e 100644
--- a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Contracts.cs
+++ b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Contracts.cs
@@ -2,7 +2,7 @@
using System.Threading;
using System.Threading.Tasks;
-namespace UiPath.CoreIpc.NodeInterop;
+namespace UiPath.Ipc.NodeInterop;
internal static class Contracts
{
diff --git a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Program.cs b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Program.cs
index 17f23329..bb5ed827 100644
--- a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Program.cs
+++ b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Program.cs
@@ -7,14 +7,15 @@
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
-using UiPath.CoreIpc.NamedPipe;
-using UiPath.CoreIpc.WebSockets;
+using UiPath.Ipc.Transport.NamedPipe;
+using UiPath.Ipc.Transport.WebSocket;
-namespace UiPath.CoreIpc.NodeInterop;
+namespace UiPath.Ipc.NodeInterop;
using static Contracts;
using static ServiceImpls;
using static Signalling;
+using static UiPath.Ipc.NodeInterop.Extensions;
class Program
{
@@ -61,6 +62,11 @@ static async Task Main(
static async Task MainCore(string? pipeName, string? webSocketUrl, int? maybeSecondsPowerOnDelay)
{
+ if (pipeName is null && webSocketUrl is null)
+ {
+ throw new ArgumentException($"At least one of {nameof(pipeName)} or {nameof(webSocketUrl)} must be specified.");
+ }
+
if (maybeSecondsPowerOnDelay is { } secondsPowerOnDelay)
{
await Task.Delay(TimeSpan.FromSeconds(secondsPowerOnDelay));
@@ -71,7 +77,6 @@ static async Task MainCore(string? pipeName, string? webSocketUrl, int? maybeSec
var sp = services
.AddLogging()
- .AddIpc()
.AddSingleton()
.AddSingleton()
.AddSingleton()
@@ -79,18 +84,27 @@ static async Task MainCore(string? pipeName, string? webSocketUrl, int? maybeSec
.AddSingleton()
.BuildServiceProvider();
- var serviceHost = new ServiceHostBuilder(sp)
- .UseNamedPipesAndOrWebSockets(pipeName, webSocketUrl)
- .AddEndpoint()
- .AddEndpoint()
- .AddEndpoint()
- .AddEndpoint()
- .AddEndpoint()
- .Build();
-
var thread = new AsyncContextThread();
thread.Context.SynchronizationContext.Send(_ => Thread.CurrentThread.Name = "GuiThread", null);
- var sched = thread.Context.Scheduler;
+ var scheduler = thread.Context.Scheduler;
+
+ var ipcServer = new IpcServer()
+ {
+ Endpoints = new()
+ {
+ typeof(IAlgebra),
+ typeof(ICalculus),
+ typeof(IBrittleService),
+ typeof(IEnvironmentVariableGetter),
+ typeof(IDtoService)
+ },
+ Listeners = [
+ ..EnumerateListeners(pipeName, webSocketUrl)
+ ],
+ ServiceProvider = sp,
+ Scheduler = scheduler
+ };
+ ipcServer.Start();
_ = Task.Run(async () =>
{
@@ -98,7 +112,6 @@ static async Task MainCore(string? pipeName, string? webSocketUrl, int? maybeSec
{
await using var sp = new ServiceCollection()
.AddLogging()
- .AddIpc()
.BuildServiceProvider();
var callback = new Arithmetic();
@@ -107,25 +120,51 @@ IEnumerable EnumeratePings()
{
if (webSocketUrl is not null)
{
- yield return new WebSocketClientBuilder(uri: new(webSocketUrl), sp)
- .RequestTimeout(TimeSpan.FromHours(5))
- .CallbackInstance(callback)
- .Build()
- .Ping();
+ yield return new IpcClient
+ {
+ Config = new()
+ {
+ ServiceProvider = sp,
+ RequestTimeout = TimeSpan.FromHours(5),
+ Callbacks = new()
+ {
+ { typeof(IArithmetic), callback }
+ },
+ },
+ Transport = new WebSocketTransport
+ {
+ Uri = new(webSocketUrl),
+ }
+ }
+ .GetProxy()
+ .Ping();
}
if (pipeName is not null)
{
- yield return new NamedPipeClientBuilder(pipeName, sp)
- .RequestTimeout(TimeSpan.FromHours(5))
- .CallbackInstance(callback)
- .Build()
- .Ping();
+ yield return new IpcClient
+ {
+ Config = new()
+ {
+ ServiceProvider = sp,
+ RequestTimeout = TimeSpan.FromHours(5),
+ Callbacks = new()
+ {
+ { typeof(IArithmetic), callback }
+ }
+ },
+ Transport = new NamedPipeTransport()
+ {
+ PipeName = pipeName,
+ }
+ }
+ .GetProxy()
+ .Ping();
}
}
await Task.WhenAll(EnumeratePings());
-
+
Send(SignalKind.ReadyToConnect);
}
catch (Exception ex)
@@ -135,7 +174,29 @@ IEnumerable EnumeratePings()
}
});
- await serviceHost.RunAsync(sched);
+ await ipcServer.WaitForStop();
+
+ IEnumerable EnumerateListeners(string? pipeName, string? webSocketUrl)
+ {
+ if (pipeName is not null)
+ {
+ yield return new NamedPipeListener() { PipeName = pipeName };
+ }
+
+ if (webSocketUrl is not null)
+ {
+ string url = CurateWebSocketUrl(webSocketUrl);
+ var accept = new HttpSysWebSocketsListener(url).Accept;
+ yield return new WebSocketListener() { Accept = accept };
+ }
+
+ static string CurateWebSocketUrl(string raw)
+ {
+ var builder = new UriBuilder(raw);
+ builder.Scheme = "http";
+ return builder.ToString();
+ }
+ }
}
private class Arithmetic : IArithmetic
@@ -148,37 +209,6 @@ private class Arithmetic : IArithmetic
internal static class Extensions
{
- public static ServiceHostBuilder UseNamedPipesAndOrWebSockets(this ServiceHostBuilder builder, string? pipeName, string? webSocketUrl)
- {
- if (pipeName is null && webSocketUrl is null)
- {
- throw new ArgumentOutOfRangeException();
- }
-
- if (pipeName is not null)
- {
- builder = builder.UseNamedPipes(new NamedPipeSettings(pipeName));
- }
-
- if (webSocketUrl is not null)
- {
- string url = CurateWebSocketUrl(webSocketUrl);
- var accept = new HttpSysWebSocketsListener(url).Accept;
- WebSocketSettings settings = new(accept);
-
- builder = builder.UseWebSockets(settings);
- }
-
- return builder;
- }
-
- private static string CurateWebSocketUrl(string raw)
- {
- var builder = new UriBuilder(raw);
- builder.Scheme = "http";
- return builder.ToString();
- }
-
public class HttpSysWebSocketsListener : IDisposable
{
HttpListener _httpListener = new();
diff --git a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/ServiceImpls.cs b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/ServiceImpls.cs
index 0a436080..3b9706f1 100644
--- a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/ServiceImpls.cs
+++ b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/ServiceImpls.cs
@@ -3,7 +3,7 @@
using System.Threading;
using System.Threading.Tasks;
-namespace UiPath.CoreIpc.NodeInterop;
+namespace UiPath.Ipc.NodeInterop;
using static Contracts;
diff --git a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Signalling.cs b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Signalling.cs
index aba44d91..76d667f4 100644
--- a/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Signalling.cs
+++ b/src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Signalling.cs
@@ -2,7 +2,7 @@
using Newtonsoft.Json.Converters;
using System;
-namespace UiPath.CoreIpc.NodeInterop;
+namespace UiPath.Ipc.NodeInterop;
internal static class Signalling
{
diff --git a/src/CoreIpc.sln b/src/CoreIpc.sln
index b69f1d5f..8198f5ab 100644
--- a/src/CoreIpc.sln
+++ b/src/CoreIpc.sln
@@ -3,41 +3,42 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31919.166
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcSample.ConsoleServer", "IpcSample.ConsoleServer\IpcSample.ConsoleServer.csproj", "{24A3C4D2-95A2-48D9-86F2-648879EC74F4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpcSample.ConsoleClient", "IpcSample.ConsoleClient\IpcSample.ConsoleClient.csproj", "{8D54E62A-ECFF-4FFF-B9D1-DB343D456451}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiPath.CoreIpc", "UiPath.CoreIpc\UiPath.CoreIpc.csproj", "{58200319-1F71-4E22-894D-7E69E0CD0B57}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiPath.CoreIpc.Tests", "UiPath.CoreIpc.Tests\UiPath.CoreIpc.Tests.csproj", "{892424AE-4D3A-4984-914E-9423BE8D0212}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{676A208A-2F08-4749-A833-F8D2BCB1B147}"
ProjectSection(SolutionItems) = preProject
- NuGet.Config = NuGet.Config
+ Directory.Build.targets = Directory.Build.targets
+ ..\NuGet.Config = ..\NuGet.Config
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playground", "Playground\Playground.csproj", "{F0365E40-DA73-4583-A363-89CBEF68A4C6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiPath.CoreIpc.Http", "UiPath.CoreIpc.Http\UiPath.CoreIpc.Http.csproj", "{8776E55A-D4EB-4C3A-8FA2-29E9A1CAE469}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiPath.Ipc.Tests", "UiPath.Ipc.Tests\UiPath.Ipc.Tests.csproj", "{E238E183-92CF-48A6-890F-C422853D6656}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {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
{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
- {892424AE-4D3A-4984-914E-9423BE8D0212}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {892424AE-4D3A-4984-914E-9423BE8D0212}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {892424AE-4D3A-4984-914E-9423BE8D0212}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {892424AE-4D3A-4984-914E-9423BE8D0212}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F0365E40-DA73-4583-A363-89CBEF68A4C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F0365E40-DA73-4583-A363-89CBEF68A4C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F0365E40-DA73-4583-A363-89CBEF68A4C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F0365E40-DA73-4583-A363-89CBEF68A4C6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8776E55A-D4EB-4C3A-8FA2-29E9A1CAE469}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8776E55A-D4EB-4C3A-8FA2-29E9A1CAE469}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8776E55A-D4EB-4C3A-8FA2-29E9A1CAE469}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8776E55A-D4EB-4C3A-8FA2-29E9A1CAE469}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E238E183-92CF-48A6-890F-C422853D6656}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E238E183-92CF-48A6-890F-C422853D6656}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E238E183-92CF-48A6-890F-C422853D6656}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E238E183-92CF-48A6-890F-C422853D6656}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
new file mode 100644
index 00000000..4979abb6
--- /dev/null
+++ b/src/Directory.Build.targets
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/IpcSample.ConsoleClient/Client.cs b/src/IpcSample.ConsoleClient/Client.cs
index 36515be2..b715e1cb 100644
--- a/src/IpcSample.ConsoleClient/Client.cs
+++ b/src/IpcSample.ConsoleClient/Client.cs
@@ -1,9 +1,9 @@
-using System.Text;
+using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
-using UiPath.CoreIpc.NamedPipe;
-using Microsoft.Extensions.DependencyInjection;
+using System.Text;
+using UiPath.Ipc.BackCompat;
-namespace UiPath.CoreIpc.Tests;
+namespace UiPath.Ipc.Tests;
class Client
{
@@ -32,7 +32,7 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
var serviceProvider = ConfigureServices();
var callback = new ComputingCallback { Id = "custom made" };
var computingClientBuilder = new NamedPipeClientBuilder("test", serviceProvider)
- .SerializeParametersAsObjects().CallbackInstance(callback).AllowImpersonation().RequestTimeout(TimeSpan.FromSeconds(2));
+ .CallbackInstance(callback).AllowImpersonation().RequestTimeout(TimeSpan.FromSeconds(2));
var stopwatch = Stopwatch.StartNew();
int count = 0;
try
@@ -40,7 +40,6 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
var computingClient = computingClientBuilder.ValidateAndBuild();
var systemClient =
new NamedPipeClientBuilder("test")
- .SerializeParametersAsObjects()
.RequestTimeout(TimeSpan.FromSeconds(2))
.Logger(serviceProvider)
.AllowImpersonation()
@@ -67,7 +66,7 @@ private static async Task RunTestsAsync(CancellationToken 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.DoNothing(cancellationToken);
+ await systemClient.FireAndForget(cancellationToken);
Console.WriteLine($"[TEST 4] invoked DoNothing()");
//((IDisposable)systemClient).Dispose();
@@ -76,7 +75,7 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
Console.WriteLine($"[TEST 5] {text}");
// test 6: call IPC service method returning GUID
- Guid generatedId = await systemClient.GetGuid(Guid.NewGuid(), cancellationToken);
+ Guid generatedId = await systemClient.EchoGuid(Guid.NewGuid(), cancellationToken);
Console.WriteLine($"[TEST 6] generated ID is: {generatedId}");
// test 7: call IPC service method with byte array
@@ -109,7 +108,7 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
{
stopwatch.Stop();
Console.WriteLine();
- Console.WriteLine("Calls per second: " + count*8 / stopwatch.Elapsed.TotalSeconds);
+ Console.WriteLine("Calls per second: " + count * 8 / stopwatch.Elapsed.TotalSeconds);
Console.WriteLine();
}
// test 10: call slow IPC service method
diff --git a/src/IpcSample.ConsoleClient/IpcSample.ConsoleClient.csproj b/src/IpcSample.ConsoleClient/IpcSample.ConsoleClient.csproj
index a313c304..646a29aa 100644
--- a/src/IpcSample.ConsoleClient/IpcSample.ConsoleClient.csproj
+++ b/src/IpcSample.ConsoleClient/IpcSample.ConsoleClient.csproj
@@ -6,10 +6,10 @@
app1.manifest
latest
true
+ enable
-
@@ -18,5 +18,9 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/IpcSample.ConsoleClient/SimpleClient.cs b/src/IpcSample.ConsoleClient/SimpleClient.cs
new file mode 100644
index 00000000..eba71a10
--- /dev/null
+++ b/src/IpcSample.ConsoleClient/SimpleClient.cs
@@ -0,0 +1,66 @@
+using Microsoft.Extensions.Logging;
+using UiPath.Ipc.Tests;
+
+namespace IpcSample.ConsoleClient;
+
+internal class SimpleClient
+{
+
+ public static async Task Entry()
+ {
+ Settings pf = new()
+ {
+ ClientTransport = new ClientTransport.NamedPipes
+ {
+ PipeName = "test",
+ AllowImpersonation = true
+ },
+ Logger = new Logger(new LoggerFactory()),
+ RequestTimeout = TimeSpan.FromSeconds(2),
+ Callback = new CallbackSource.Instance
+ {
+ CallbackInstance = new ComputingCallback { Id = "custom made" }
+ }
+ };
+
+ var cs = pf.Build();
+ // -----------
+ }
+
+ class Settings
+ {
+ public required ClientTransport ClientTransport { get; init; }
+ public TimeSpan RequestTimeout { get; init; } = Timeout.InfiniteTimeSpan;
+ public required ILogger Logger { get; init; }
+ public CallbackSource? Callback { get; init; }
+
+ public T Build() where T : class
+ {
+ throw null!;
+ }
+ }
+
+ abstract class CallbackSource
+ {
+ public class Injected : CallbackSource
+ {
+ public required IServiceProvider ServiceProvider { get; init; }
+ public required Type CallbackType { get; init; }
+ }
+
+ public class Instance : CallbackSource
+ {
+ public required object CallbackInstance { get; init; }
+ }
+ }
+
+ abstract class ClientTransport
+ {
+ public class NamedPipes : ClientTransport
+ {
+ public required string PipeName { get; init; }
+ public bool AllowImpersonation { get; init; }
+ }
+ }
+
+}
diff --git a/src/IpcSample.ConsoleClient/TcpClient.cs b/src/IpcSample.ConsoleClient/TcpClient.cs
index 0768ebc6..0e5b33ed 100644
--- a/src/IpcSample.ConsoleClient/TcpClient.cs
+++ b/src/IpcSample.ConsoleClient/TcpClient.cs
@@ -1,10 +1,10 @@
using System.Text;
using System.Diagnostics;
-using UiPath.CoreIpc.Tcp;
+using UiPath.Ipc.BackCompat;
using Microsoft.Extensions.DependencyInjection;
using System.Net;
-namespace UiPath.CoreIpc.Tests;
+namespace UiPath.Ipc.Tests;
class TcpClient
{
@@ -35,7 +35,7 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
var serviceProvider = ConfigureServices();
var callback = new ComputingCallback { Id = "custom made" };
var computingClientBuilder = new TcpClientBuilder(SystemEndPoint, serviceProvider)
- .SerializeParametersAsObjects().CallbackInstance(callback)/*.EncryptAndSign("localhost")*/.RequestTimeout(TimeSpan.FromSeconds(2));
+ .CallbackInstance(callback)/*.EncryptAndSign("localhost")*/.RequestTimeout(TimeSpan.FromSeconds(2));
var stopwatch = Stopwatch.StartNew();
int count = 0;
try
@@ -43,7 +43,6 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
var computingClient = computingClientBuilder.ValidateAndBuild();
var systemClient =
new TcpClientBuilder(SystemEndPoint)
- .SerializeParametersAsObjects()
//.EncryptAndSign("localhost")
.RequestTimeout(TimeSpan.FromSeconds(2))
.Logger(serviceProvider)
@@ -84,7 +83,7 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
Console.WriteLine($"[TEST 5] {text}");
// test 6: call IPC service method returning GUID
- Guid generatedId = await systemClient.GetGuid(Guid.NewGuid(), cancellationToken);
+ Guid generatedId = await systemClient.EchoGuid(Guid.NewGuid(), cancellationToken);
Console.WriteLine($"[TEST 6] generated ID is: {generatedId}");
// test 7: call IPC service method with byte array
diff --git a/src/IpcSample.ConsoleClient/WebSocketClient.cs b/src/IpcSample.ConsoleClient/WebSocketClient.cs
index e6c93422..c9e1afbc 100644
--- a/src/IpcSample.ConsoleClient/WebSocketClient.cs
+++ b/src/IpcSample.ConsoleClient/WebSocketClient.cs
@@ -1,8 +1,10 @@
-using System.Text;
+using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
-using UiPath.CoreIpc.WebSockets;
-using Microsoft.Extensions.DependencyInjection;
-namespace UiPath.CoreIpc.Tests;
+using System.Text;
+using UiPath.Ipc.BackCompat;
+
+namespace UiPath.Ipc.Tests;
+
class WebSocketClient
{
static async Task _Main(string[] args)
@@ -32,7 +34,7 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
Uri uri = new("ws://localhost:1212/wsDemo/");
var serviceProvider = ConfigureServices();
var callback = new ComputingCallback { Id = "custom made" };
- var computingClientBuilder = new WebSocketClientBuilder(uri, serviceProvider).SerializeParametersAsObjects()
+ var computingClientBuilder = new WebSocketClientBuilder(uri, serviceProvider)
.CallbackInstance(callback)/*.EncryptAndSign("localhost")*/.RequestTimeout(TimeSpan.FromSeconds(2));
var stopwatch = Stopwatch.StartNew();
int count = 0;
@@ -40,7 +42,7 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
{
var computingClient = computingClientBuilder.ValidateAndBuild();
var systemClient =
- new WebSocketClientBuilder(uri).SerializeParametersAsObjects()
+ new WebSocketClientBuilder(uri)
//.EncryptAndSign("localhost")
.RequestTimeout(TimeSpan.FromSeconds(2))
.Logger(serviceProvider)
@@ -81,7 +83,7 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
Console.WriteLine($"[TEST 5] {text}");
// test 6: call IPC service method returning GUID
- Guid generatedId = await systemClient.GetGuid(Guid.NewGuid(), cancellationToken);
+ Guid generatedId = await systemClient.EchoGuid(Guid.NewGuid(), cancellationToken);
Console.WriteLine($"[TEST 6] generated ID is: {generatedId}");
// test 7: call IPC service method with byte array
diff --git a/src/IpcSample.ConsoleServer/Server.cs b/src/IpcSample.ConsoleServer/Server.cs
index 8e9ad9e4..87e6c9ca 100644
--- a/src/IpcSample.ConsoleServer/Server.cs
+++ b/src/IpcSample.ConsoleServer/Server.cs
@@ -1,8 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
-using UiPath.CoreIpc.NamedPipe;
+using UiPath.Ipc.BackCompat;
+using UiPath.Ipc.Transport.NamedPipe;
-namespace UiPath.CoreIpc.Tests;
+namespace UiPath.Ipc.Tests;
class Server
{
@@ -21,20 +22,21 @@ static async Task Main()
var serviceProvider = ConfigureServices();
// build and run service host
var host = new ServiceHostBuilder(serviceProvider)
- .UseNamedPipes(new NamedPipeSettings("test")
+ .UseNamedPipes(new NamedPipeListener()
{
+ PipeName = "test",
RequestTimeout = TimeSpan.FromSeconds(2),
//AccessControl = security => security.AllowCurrentUser(),
})
- .AddEndpoint()
+ .AddEndpoint()
.AddEndpoint()
.ValidateAndBuild();
- await await Task.WhenAny(host.RunAsync(), Task.Run(() =>
+ await await Task.WhenAny(host.RunAsync(), Task.Run(async () =>
{
Console.WriteLine(typeof(int).Assembly);
Console.ReadLine();
- host.Dispose();
+ await host.DisposeAsync();
}));
Console.WriteLine("Server stopped.");
diff --git a/src/IpcSample.ConsoleServer/TcpServer.cs b/src/IpcSample.ConsoleServer/TcpServer.cs
index 172a7725..a89cec24 100644
--- a/src/IpcSample.ConsoleServer/TcpServer.cs
+++ b/src/IpcSample.ConsoleServer/TcpServer.cs
@@ -1,9 +1,12 @@
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
-using System.Net;
-using UiPath.CoreIpc.Tcp;
+using UiPath.Ipc.BackCompat;
+using UiPath.Ipc.Transport.Tcp;
-namespace UiPath.CoreIpc.Tests;
+namespace UiPath.Ipc.Tests;
+
+using IPEndPoint = System.Net.IPEndPoint;
+using IPAddress = System.Net.IPAddress;
class TcpServer
{
@@ -25,20 +28,21 @@ static async Task _Main()
// build and run service host
var data = File.ReadAllBytes(@"../../../../localhost.pfx");
var host = new ServiceHostBuilder(serviceProvider)
- .UseTcp(new TcpSettings(SystemEndPoint)
+ .UseTcp(new TcpListener()
{
+ EndPoint = SystemEndPoint,
RequestTimeout = TimeSpan.FromSeconds(2),
//Certificate = new X509Certificate(data, "1"),
})
- .AddEndpoint()
+ .AddEndpoint()
.AddEndpoint()
.ValidateAndBuild();
- await await Task.WhenAny(host.RunAsync(), Task.Run(() =>
+ await await Task.WhenAny(host.RunAsync(), Task.Run(async () =>
{
Console.WriteLine(typeof(int).Assembly);
Console.ReadLine();
- host.Dispose();
+ await host.DisposeAsync();
}));
Console.WriteLine("Server stopped.");
diff --git a/src/IpcSample.ConsoleServer/WebSocketServer.cs b/src/IpcSample.ConsoleServer/WebSocketServer.cs
index 322c1d65..8a52c013 100644
--- a/src/IpcSample.ConsoleServer/WebSocketServer.cs
+++ b/src/IpcSample.ConsoleServer/WebSocketServer.cs
@@ -1,9 +1,8 @@
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
-using System.Net;
-using System.Net.WebSockets;
-using UiPath.CoreIpc.WebSockets;
-namespace UiPath.CoreIpc.Tests;
+using UiPath.Ipc.BackCompat;
+using UiPath.Ipc.Transport.WebSocket;
+namespace UiPath.Ipc.Tests;
class WebSocketServer
{
//private static readonly Timer _timer = new Timer(_ =>
@@ -23,19 +22,20 @@ static async Task _Main()
// build and run service host
//var data = File.ReadAllBytes(@"../../../../localhost.pfx");
var host = new ServiceHostBuilder(serviceProvider)
- .UseWebSockets(new(new HttpSysWebSocketsListener("http://localhost:1212/wsDemo/").Accept)
+ .UseWebSockets(new WebSocketListener()
{
+ Accept = new HttpSysWebSocketsListener("http://localhost:1212/wsDemo/").Accept,
RequestTimeout = TimeSpan.FromSeconds(2),
//Certificate = new X509Certificate(data, "1"),
})
- .AddEndpoint()
+ .AddEndpoint()
.AddEndpoint()
.ValidateAndBuild();
- await await Task.WhenAny(host.RunAsync(), Task.Run(() =>
+ await await Task.WhenAny(host.RunAsync(), Task.Run(async () =>
{
Console.WriteLine(typeof(int).Assembly);
Console.ReadLine();
- host.Dispose();
+ await host.DisposeAsync();
}));
Console.WriteLine("Server stopped.");
return;
diff --git a/src/Playground/Contracts.cs b/src/Playground/Contracts.cs
new file mode 100644
index 00000000..dd847ede
--- /dev/null
+++ b/src/Playground/Contracts.cs
@@ -0,0 +1,24 @@
+using UiPath.Ipc;
+
+namespace Playground;
+
+public static class Contracts
+{
+ public const string PipeName = "SomePipe";
+
+ public interface IServerOperations
+ {
+ Task Register(Message? m = null);
+ Task Broadcast(string text);
+ }
+
+ public interface IClientOperations
+ {
+ Task Greet(string text);
+ }
+
+ public interface IClientOperations2
+ {
+ Task GetTheTime();
+ }
+}
diff --git a/src/Playground/Impl.cs b/src/Playground/Impl.cs
new file mode 100644
index 00000000..4d020920
--- /dev/null
+++ b/src/Playground/Impl.cs
@@ -0,0 +1,68 @@
+using System.Collections.Concurrent;
+using UiPath.Ipc;
+
+namespace Playground;
+
+internal static class Impl
+{
+ public sealed class ClientRegistry
+ {
+ private readonly ConcurrentDictionary _clients = new();
+
+ public bool Add(ClientPair pair) => _clients.TryAdd(pair, value: null);
+
+ public IReadOnlyList All() => _clients.Keys.ToArray();
+ }
+
+ public readonly record struct ClientPair(Contracts.IClientOperations Client, Contracts.IClientOperations2 Client2);
+
+ public sealed class Server(ClientRegistry clients) : Contracts.IServerOperations
+ {
+ public async Task Register(Message? m = null)
+ {
+ var clientOps = m!.GetCallback();
+ var clientOps2 = m.GetCallback();
+
+ var added = clients.Add(new(clientOps, clientOps2));
+
+ if (added)
+ {
+ Console.WriteLine("New client registered.");
+ }
+ else
+ {
+ Console.WriteLine("Client tried to register again resulting in a NOP.");
+ }
+
+ return true;
+ }
+
+ public async Task Broadcast(string text)
+ {
+ var pairs = clients.All();
+
+ foreach (var pair in pairs)
+ {
+ var time = await pair.Client2.GetTheTime();
+ _ = await pair.Client.Greet($"{text} - You said the time was: {time}");
+ }
+
+ return true;
+ }
+ }
+
+ public sealed class ClientOperations() : Contracts.IClientOperations
+ {
+ public async Task Greet(string text)
+ {
+ Console.WriteLine($"Scheduler: {TaskScheduler.Current.GetType().Name}");
+ Console.WriteLine($"Server says: {text}");
+ return true;
+ }
+ }
+
+ public sealed class Client2 : Contracts.IClientOperations2
+ {
+ public Task GetTheTime() => Task.FromResult(DateTime.Now);
+ }
+}
diff --git a/src/Playground/Playground.csproj b/src/Playground/Playground.csproj
new file mode 100644
index 00000000..760afbc7
--- /dev/null
+++ b/src/Playground/Playground.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Playground/Program.cs b/src/Playground/Program.cs
new file mode 100644
index 00000000..86bb8470
--- /dev/null
+++ b/src/Playground/Program.cs
@@ -0,0 +1,150 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Playground;
+using UiPath.Ipc;
+using UiPath.Ipc.Transport.NamedPipe;
+
+internal class Program
+{
+ private static async Task Main(string[] args)
+ {
+ using var cts = new CancellationTokenSource();
+ Console.CancelKeyPress += (s, e) =>
+ {
+ e.Cancel = true;
+ cts.Cancel();
+ };
+
+ Uri serverUri = new("http://localhost:62234");
+ Uri clientUri = new("http://localhost:62235");
+
+ var cancelled = Task.Delay(Timeout.InfiniteTimeSpan, cts.Token);
+
+ var serverScheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
+ var clientScheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
+
+ await using var serverSP = new ServiceCollection()
+ .AddSingleton()
+ .AddScoped()
+ .AddLogging(builder => builder.AddConsole())
+ .BuildServiceProvider();
+
+ await using var clientSP = new ServiceCollection()
+ .AddScoped()
+ .AddLogging(builder => builder.AddConsole())
+ .BuildServiceProvider();
+
+ await using var ipcServer = new IpcServer()
+ {
+ Scheduler = serverScheduler,
+ ServiceProvider = serverSP,
+ Endpoints = new()
+ {
+ typeof(Contracts.IServerOperations), // DEVINE
+ new EndpointSettings(typeof(Contracts.IServerOperations)) // ASTALALT
+ {
+ BeforeCall = async (callInfo, _) =>
+ {
+ Console.WriteLine($"Server: {callInfo.Method.Name}");
+ }
+ },
+ typeof(Contracts.IClientOperations2)
+ },
+ Listeners = [
+ new NamedPipeListener()
+ {
+ PipeName = Contracts.PipeName,
+ ServerName = ".",
+ AccessControl = ps =>
+ {
+ },
+ MaxReceivedMessageSizeInMegabytes = 100,
+ RequestTimeout = TimeSpan.FromHours(10)
+ },
+ //new BidirectionalHttp.ListenerConfig()
+ //{
+ // Uri = serverUri,
+ // RequestTimeout = TimeSpan.FromHours(1)
+ //}
+ ]
+ };
+
+ try
+ {
+ ipcServer.Start(); // ar putea fi void, ar putea fi si Run
+ // await ipcServer.WaitForStart();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Failed to start.");
+ Console.WriteLine(ex.ToString());
+ throw;
+ }
+
+ var c1 = new IpcClient()
+ {
+ Config = new()
+ {
+ Callbacks = new()
+ {
+ typeof(Contracts.IClientOperations),
+ { typeof(Contracts.IClientOperations2), new Impl.Client2() },
+ },
+ ServiceProvider = clientSP,
+ Scheduler = clientScheduler,
+ },
+ Transport = new NamedPipeTransport()
+ {
+ PipeName = Contracts.PipeName,
+ ServerName = ".",
+ AllowImpersonation = false,
+ },
+ };
+
+ var c2 = new IpcClient()
+ {
+ Config = new()
+ {
+ ServiceProvider = clientSP,
+ Callbacks = new()
+ {
+ typeof(Contracts.IClientOperations),
+ { typeof(Contracts.IClientOperations2), new Impl.Client2() },
+ },
+ Scheduler = clientScheduler,
+ },
+ Transport = new NamedPipeTransport()
+ {
+ PipeName = Contracts.PipeName,
+ ServerName = ".",
+ AllowImpersonation = false,
+ },
+ };
+
+ var proxy1 = new IpcClient()
+ {
+ Config = new()
+ {
+ ServiceProvider = clientSP,
+ Callbacks = new()
+ {
+ typeof(Contracts.IClientOperations),
+ { typeof(Contracts.IClientOperations2), new Impl.Client2() },
+ },
+ Scheduler = clientScheduler,
+ },
+ Transport = new NamedPipeTransport()
+ {
+ PipeName = Contracts.PipeName,
+ ServerName = ".",
+ AllowImpersonation = false,
+ },
+ }.GetProxy();
+
+
+ await proxy1.Register();
+ await proxy1.Broadcast("Hello Bidirectional Http!");
+
+ await Task.WhenAny(cancelled);
+ }
+}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Http/BidiHttpListener.cs b/src/UiPath.CoreIpc.Http/BidiHttpListener.cs
new file mode 100644
index 00000000..923421bd
--- /dev/null
+++ b/src/UiPath.CoreIpc.Http/BidiHttpListener.cs
@@ -0,0 +1,286 @@
+using Nito.AsyncEx;
+using System;
+using System.Buffers;
+using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
+using System.IO.Pipelines;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Channels;
+
+namespace UiPath.Ipc.Http;
+
+using static Constants;
+using IBidiHttpListenerConfig = IListenerConfig;
+
+public sealed partial record BidiHttpListener : ListenerConfig, IBidiHttpListenerConfig
+{
+ public required Uri Uri { get; init; }
+
+ BidiHttpListenerState IBidiHttpListenerConfig.CreateListenerState(IpcServer server)
+ => new(server, this);
+
+ BidiHttpServerConnectionState IBidiHttpListenerConfig.CreateConnectionState(IpcServer server, BidiHttpListenerState listenerState)
+ => new(server, listenerState);
+
+ async ValueTask IBidiHttpListenerConfig.AwaitConnection(BidiHttpListenerState listenerState, BidiHttpServerConnectionState connectionState, CancellationToken ct)
+ {
+ await connectionState.WaitForConnection(ct);
+ return connectionState;
+ }
+
+ public IEnumerable Validate()
+ {
+ throw new NotImplementedException();
+ }
+}
+
+internal sealed class BidiHttpListenerState : IAsyncDisposable
+{
+ private readonly IpcServer _ipcServer;
+ private readonly CancellationTokenSource _cts = new();
+ private readonly HttpListener _httpListener;
+ private readonly Task _processing;
+ private readonly Lazy _disposing;
+
+ private readonly ConcurrentDictionary> _connections = new();
+ private readonly Channel<(Guid connectionId, Uri reverseUri)> _newConnections = Channel.CreateUnbounded<(Guid connectionId, Uri reverseUri)>();
+
+ public ChannelReader<(Guid connectionId, Uri reverseUri)> NewConnections => _newConnections.Reader;
+ public ChannelReader GetConnectionChannel(Guid connectionId) => _connections[connectionId];
+
+ public BidiHttpListenerState(IpcServer ipcServer, BidiHttpListener listener)
+ {
+ _ipcServer = ipcServer;
+ _httpListener = new HttpListener()
+ {
+ Prefixes =
+ {
+ listener.Uri.ToString()
+ }
+ };
+ _processing = ProcessContexts();
+ _disposing = new(DisposeCore);
+ }
+
+ public ValueTask DisposeAsync() => new(_disposing.Value);
+
+ private async Task DisposeCore()
+ {
+ _cts.Cancel();
+ try
+ {
+ await _processing;
+ }
+ catch (OperationCanceledException ex) when (ex.CancellationToken == _cts.Token)
+ {
+ }
+
+ foreach (var pair in _connections)
+ {
+ pair.Value.Writer.Complete();
+ }
+ _cts.Dispose();
+ }
+
+ private async Task ProcessContexts()
+ {
+ await foreach (var (context, connectionId, reverseUri) in AwaitContexts())
+ {
+ var connectionChannel = _connections.GetOrAdd(connectionId, _ =>
+ {
+ _newConnections.Writer.TryWrite((connectionId, reverseUri));
+ return Channel.CreateUnbounded();
+ });
+
+ await connectionChannel.Writer.WriteAsync(context, _cts.Token);
+ }
+
+ async IAsyncEnumerable<(HttpListenerContext context, Guid connectionId, Uri reverseUri)> AwaitContexts()
+ {
+ while (!_cts.Token.IsCancellationRequested)
+ {
+ var context = await _httpListener.GetContextAsync();
+
+ if (!TryAcceptContext(context, out var connectionId, out var reverseUri))
+ {
+ context.Response.StatusCode = 400;
+ context.Response.Close();
+ continue;
+ }
+
+ yield return (context, connectionId, reverseUri);
+ }
+ }
+
+ bool TryAcceptContext(HttpListenerContext context, out Guid connectionId, [NotNullWhen(returnValue: true)] out Uri? reverseUri)
+ {
+ if (!Guid.TryParse(context.Request.Headers[ConnectionIdHeader], out connectionId) ||
+ !Uri.TryCreate(context.Request.Headers[ReverseUriHeader], UriKind.Absolute, out reverseUri))
+ {
+ connectionId = Guid.Empty;
+ reverseUri = null;
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
+
+internal sealed class BidiHttpServerConnectionState : Stream, IAsyncDisposable
+{
+ private readonly Pipe _pipe = new();
+
+ private readonly IpcServer _server;
+ private readonly BidiHttpListenerState _listenerState;
+
+ private readonly CancellationTokenSource _cts = new();
+ private readonly AsyncLock _lock = new();
+ private (Guid connectionId, Uri reverseUri)? _connection = null;
+ private HttpClient? _client;
+ private Task? _processing = null;
+ private readonly Lazy _disposing;
+
+ public BidiHttpServerConnectionState(IpcServer server, BidiHttpListenerState listenerState)
+ {
+ _server = server;
+ _listenerState = listenerState;
+ _disposing = new(DisposeCore);
+ }
+
+ public
+#if !NET461
+ override
+#endif
+ ValueTask DisposeAsync() => new(_disposing.Value);
+
+ private async Task DisposeCore()
+ {
+ _cts.Cancel();
+
+ _client?.Dispose();
+
+ try
+ {
+ await (_processing ?? Task.CompletedTask);
+ }
+ catch (OperationCanceledException ex) when (ex.CancellationToken == _cts.Token)
+ {
+ // ignored
+ }
+
+ _cts.Dispose();
+ }
+
+ public async Task WaitForConnection(CancellationToken ct)
+ {
+ using (await _lock.LockAsync(ct))
+ {
+ if (_connection is not null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ _connection = await _listenerState.NewConnections.ReadAsync(ct);
+
+ _client = new()
+ {
+ BaseAddress = _connection.Value.reverseUri,
+ DefaultRequestHeaders =
+ {
+ { ConnectionIdHeader, _connection.Value.connectionId.ToString() }
+ }
+ };
+
+ _processing = ProcessContexts(_cts.Token);
+ }
+ }
+
+ private async Task ProcessContexts(CancellationToken ct)
+ {
+ var reader = _listenerState.GetConnectionChannel(_connection!.Value.connectionId);
+
+ while (await reader.WaitToReadAsync(ct))
+ {
+ if (!reader.TryRead(out var context))
+ {
+ continue;
+ }
+ await ProcessContext(context);
+ }
+
+ async Task ProcessContext(HttpListenerContext context)
+ {
+ try
+ {
+ while (true)
+ {
+ var memory = _pipe.Writer.GetMemory();
+ var cbRead = await context.Request.InputStream.ReadAsync(memory, ct);
+ if (cbRead is 0)
+ {
+ break;
+ }
+ _pipe.Writer.Advance(cbRead);
+ var flushResult = await _pipe.Writer.FlushAsync(ct);
+ if (flushResult.IsCompleted)
+ {
+ break;
+ }
+ }
+ }
+ finally
+ {
+ context.Response.StatusCode = 200;
+ context.Response.Close();
+ }
+ }
+ }
+
+ public override bool CanRead => true;
+ public override bool CanSeek => false;
+ public override bool CanWrite => true;
+
+ public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken ct)
+ {
+ var memory = new Memory(buffer, offset, count);
+ var readResult = await _pipe.Reader.ReadAsync(ct);
+
+ var take = (int)Math.Min(readResult.Buffer.Length, memory.Length);
+
+ readResult.Buffer.Slice(start: 0, length: take).CopyTo(memory.Span);
+ _pipe.Reader.AdvanceTo(readResult.Buffer.GetPosition(take));
+
+ return take;
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken ct)
+ {
+ var memory = new ReadOnlyMemory(buffer, offset, count);
+ if (_client is null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ HttpContent content =
+#if NET461
+ new ByteArrayContent(memory.ToArray());
+#else
+ new ReadOnlyMemoryContent(memory);
+#endif
+
+ await _client.PostAsync(requestUri: "", content, ct);
+ }
+
+ public override Task FlushAsync(CancellationToken cancellationToken)
+ => Task.CompletedTask;
+
+ public override void Flush() => throw new NotImplementedException();
+ public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();
+ public override void SetLength(long value) => throw new NotImplementedException();
+ public override int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException();
+ public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
+ public override long Length => throw new NotImplementedException();
+ public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+}
diff --git a/src/UiPath.CoreIpc.Http/Constants.cs b/src/UiPath.CoreIpc.Http/Constants.cs
new file mode 100644
index 00000000..c04e37b7
--- /dev/null
+++ b/src/UiPath.CoreIpc.Http/Constants.cs
@@ -0,0 +1,8 @@
+namespace UiPath.Ipc.Http;
+
+internal static class Constants
+{
+ internal const string ConnectionIdHeader = "X-UiPathIpc-ConnectionId";
+ internal const string ReverseUriHeader = "X-UiPathIpc-ReverseUri";
+}
+
diff --git a/src/UiPath.CoreIpc.Http/GlobalUsings.cs b/src/UiPath.CoreIpc.Http/GlobalUsings.cs
new file mode 100644
index 00000000..a7770972
--- /dev/null
+++ b/src/UiPath.CoreIpc.Http/GlobalUsings.cs
@@ -0,0 +1 @@
+global using UiPath.Ipc.Extensibility;
diff --git a/src/UiPath.CoreIpc.Http/UiPath.CoreIpc.Http.csproj b/src/UiPath.CoreIpc.Http/UiPath.CoreIpc.Http.csproj
new file mode 100644
index 00000000..524ffbc1
--- /dev/null
+++ b/src/UiPath.CoreIpc.Http/UiPath.CoreIpc.Http.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net6.0;net461;net6.0-windows
+ enable
+ enable
+ preview
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UiPath.CoreIpc.Tests/ComputingTests.cs b/src/UiPath.CoreIpc.Tests/ComputingTests.cs
deleted file mode 100644
index 6c562dd9..00000000
--- a/src/UiPath.CoreIpc.Tests/ComputingTests.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-namespace UiPath.CoreIpc.Tests;
-
-public abstract class ComputingTests : TestBase where TBuilder : ServiceClientBuilder
-{
- protected readonly ServiceHost _computingHost;
- protected readonly IComputingService _computingClient;
- protected readonly ComputingService _computingService;
- protected readonly ComputingCallback _computingCallback;
- public ComputingTests()
- {
- _computingCallback = new ComputingCallback { Id = Guid.NewGuid().ToString() };
- _computingService = (ComputingService)_serviceProvider.GetService();
- _computingHost = Configure(new ServiceHostBuilder(_serviceProvider))
- .AddEndpoint()
- .ValidateAndBuild();
- _computingHost.RunAsync(GuiScheduler);
- _computingClient = ComputingClientBuilder(GuiScheduler).SerializeParametersAsObjects().ValidateAndBuild();
- }
- protected abstract TBuilder ComputingClientBuilder(TaskScheduler taskScheduler = null);
- [Fact]
- public async Task ReconnectWithEncrypt()
- {
- for (int i = 0; i < 50; i++)
- {
- await _computingClient.AddFloat(1, 2);
- ((IpcProxy)_computingClient).CloseConnection();
- await _computingClient.AddFloat(1, 2);
- }
- }
-
- [Fact]
- public async Task AddFloat()
- {
- var result = await _computingClient.AddFloat(1.23f, 4.56f);
- result.ShouldBe(5.79f);
- }
-
- [Fact]
- public Task AddFloatConcurrently() => Task.WhenAll(Enumerable.Range(1, 100).Select(_ => AddFloat()));
-
- [Fact]
- public async Task AddComplexNumber()
- {
- var result = await _computingClient.AddComplexNumber(new ComplexNumber(1f, 3f), new ComplexNumber(2f, 5f));
- result.ShouldBe(new ComplexNumber(3f, 8f));
- }
-
- [Fact]
- public async Task ClientCancellation()
- {
- using (var cancellationSource = new CancellationTokenSource(10))
- {
- _computingClient.Infinite(cancellationSource.Token).ShouldThrow();
- }
- await AddFloat();
- }
-
- [Fact]
- public async Task ClientTimeout()
- {
- var proxy = ComputingClientBuilder().SerializeParametersAsObjects().RequestTimeout(TimeSpan.FromMilliseconds(10)).ValidateAndBuild();
- proxy.Infinite().ShouldThrow().Message.ShouldBe($"{nameof(_computingClient.Infinite)} timed out.");
- await proxy.GetCallbackThreadName(new Message { RequestTimeout = RequestTimeout });
- ((IDisposable)proxy).Dispose();
- ((IpcProxy)proxy).CloseConnection();
- }
-
- [Fact]
- public async Task TimeoutPerRequest()
- {
- for (int i = 0; i < 20; i++)
- {
- var request = new SystemMessage { RequestTimeout = TimeSpan.FromTicks(10), Delay = 100 };
- Exception exception = null;
- try
- {
- await _computingClient.SendMessage(request);
- }
- catch (TimeoutException ex)
- {
- exception = ex;
- }
- catch (RemoteException ex)
- {
- exception = ex;
- ex.Is().ShouldBeTrue();
- }
- exception.Message.ShouldBe($"{nameof(_computingClient.SendMessage)} timed out.");
- await AddFloat();
- }
- }
-
- [Fact]
- public Task InfiniteVoid() => _computingClient.InfiniteVoid();
-
- [Fact]
- public async Task AddComplexNumbers()
- {
- var result = await _computingClient.AddComplexNumbers(new[]
- {
- new ComplexNumber(0.5f, 0.4f),
- new ComplexNumber(0.2f, 0.1f),
- new ComplexNumber(0.3f, 0.5f),
- });
- result.ShouldBe(new ComplexNumber(1f, 1f));
- }
-
- [Fact]
- public async Task GetCallbackThreadName() => (await _computingClient.GetCallbackThreadName()).ShouldBe("GuiThread");
-
- [Fact]
- public Task CallbackConcurrently() => Task.WhenAll(Enumerable.Range(1, 50).Select(_ => Callback()));
-
- [Fact]
- public async Task Callback()
- {
- var message = new SystemMessage { Text = Guid.NewGuid().ToString() };
- var returnValue = await _computingClient.SendMessage(message);
- returnValue.ShouldBe($"{Environment.UserName}_{_computingCallback.Id}_{message.Text}");
- }
-
- public override void Dispose()
- {
- ((IDisposable)_computingClient).Dispose();
- ((IpcProxy)_computingClient).CloseConnection();
- _computingHost.Dispose();
- base.Dispose();
- }
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/EndpointTests.cs b/src/UiPath.CoreIpc.Tests/EndpointTests.cs
deleted file mode 100644
index e62db908..00000000
--- a/src/UiPath.CoreIpc.Tests/EndpointTests.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-namespace UiPath.CoreIpc.Tests;
-
-public class EndpointTests : IDisposable
-{
- private static TimeSpan RequestTimeout => TestBase.RequestTimeout;
- private readonly ServiceHost _host;
- private readonly IComputingService _computingClient;
- private readonly ISystemService _systemClient;
- private readonly ComputingService _computingService;
- private readonly SystemService _systemService;
- private readonly ComputingCallback _computingCallback;
- private readonly SystemCallback _systemCallback;
- private readonly IServiceProvider _serviceProvider;
- public EndpointTests()
- {
- _computingCallback = new ComputingCallback { Id = Guid.NewGuid().ToString() };
- _systemCallback = new SystemCallback { Id = Guid.NewGuid().ToString() };
- _serviceProvider = IpcHelpers.ConfigureServices();
- _computingService = (ComputingService)_serviceProvider.GetService();
- _systemService = (SystemService)_serviceProvider.GetService();
- _host = new ServiceHostBuilder(_serviceProvider)
- .UseNamedPipes(new NamedPipeSettings(PipeName) { RequestTimeout = RequestTimeout })
- .AddEndpoint()
- .AddEndpoint()
- .AddEndpoint()
- .ValidateAndBuild();
- _host.RunAsync();
- _computingClient = ComputingClientBuilder().ValidateAndBuild();
- _systemClient = CreateSystemService();
- }
- public string PipeName => nameof(EndpointTests)+GetHashCode();
- private NamedPipeClientBuilder ComputingClientBuilder(TaskScheduler taskScheduler = null) =>
- new NamedPipeClientBuilder(PipeName, _serviceProvider)
- .AllowImpersonation()
- .RequestTimeout(RequestTimeout)
- .CallbackInstance(_computingCallback)
- .SerializeParametersAsObjects()
- .TaskScheduler(taskScheduler);
- private ISystemService CreateSystemService() => SystemClientBuilder().ValidateAndBuild();
- private NamedPipeClientBuilder SystemClientBuilder() =>
- new NamedPipeClientBuilder(PipeName, _serviceProvider)
- .CallbackInstance(_systemCallback)
- .SerializeParametersAsObjects()
- .RequestTimeout(RequestTimeout)
- .AllowImpersonation();
- public void Dispose()
- {
- ((IDisposable)_computingClient).Dispose();
- ((IDisposable)_systemClient).Dispose();
- ((IpcProxy)_computingClient).CloseConnection();
- ((IpcProxy)_systemClient).CloseConnection();
- _host.Dispose();
- }
- [Fact]
- public Task CallbackConcurrently() => Task.WhenAll(Enumerable.Range(1, 50).Select(_ => CallbackCore()));
- [Fact]
- public async Task Callback()
- {
- for (int index = 0; index < 50; index++)
- {
- await CallbackCore();
- ((IpcProxy)_computingClient).CloseConnection();
- }
- }
-
- private async Task CallbackCore()
- {
- var proxy = new NamedPipeClientBuilder(PipeName)
- .SerializeParametersAsObjects().RequestTimeout(RequestTimeout).AllowImpersonation().ValidateAndBuild();
- var message = new SystemMessage { Text = Guid.NewGuid().ToString() };
- var computingTask = _computingClient.SendMessage(message);
- var systemTask = _systemClient.SendMessage(message);
- var computingBaseTask = proxy.AddFloat(1, 2);
- await Task.WhenAll(computingTask, systemTask, computingBaseTask);
- systemTask.Result.ShouldBe($"{Environment.UserName}_{_systemCallback.Id}_{message.Text}");
- computingTask.Result.ShouldBe($"{Environment.UserName}_{_computingCallback.Id}_{message.Text}");
- computingBaseTask.Result.ShouldBe(3);
- }
-
- [Fact]
- public async Task MissingCallback()
- {
- RemoteException exception = null;
- try
- {
- await _systemClient.MissingCallback(new SystemMessage());
- }
- catch (RemoteException ex)
- {
- exception = ex;
- }
- exception.Message.ShouldBe("Callback contract mismatch. Requested System.IDisposable, but it's UiPath.CoreIpc.Tests.ISystemCallback.");
- exception.Is().ShouldBeTrue();
- }
- [Fact]
- public Task CancelServerCall() => CancelServerCallCore(10);
-
- async Task CancelServerCallCore(int counter)
- {
- for (int i = 0; i < counter; i++)
- {
- var request = new SystemMessage { RequestTimeout = Timeout.InfiniteTimeSpan, Delay = Timeout.Infinite };
- Task sendMessageResult;
- using (var cancellationSource = new CancellationTokenSource())
- {
- sendMessageResult = _systemClient.MissingCallback(request, cancellationSource.Token);
- var newGuid = Guid.NewGuid();
- (await _systemClient.GetGuid(newGuid)).ShouldBe(newGuid);
- await Task.Delay(1);
- cancellationSource.Cancel();
- sendMessageResult.ShouldThrow();
- newGuid = Guid.NewGuid();
- (await _systemClient.GetGuid(newGuid)).ShouldBe(newGuid);
- }
- ((IDisposable)_systemClient).Dispose();
- }
- }
-
- [Fact]
- public async Task DuplicateCallbackProxies()
- {
- await _systemClient.GetThreadName();
- var proxy = CreateSystemService();
- var message = proxy.GetThreadName().ShouldThrow().Message;
- message.ShouldStartWith("Duplicate callback proxy instance EndpointTests");
- message.ShouldEndWith(". Consider using a singleton callback proxy.");
- }
-}
-public interface ISystemCallback
-{
- Task GetId(Message message = null);
-}
-public class SystemCallback : ISystemCallback
-{
- public string Id { get; set; }
- public async Task GetId(Message message)
- {
- message.Client.ShouldBeNull();
- return Id;
- }
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/Implementation/ComputingCallback.cs b/src/UiPath.CoreIpc.Tests/Implementation/ComputingCallback.cs
deleted file mode 100644
index 7938f812..00000000
--- a/src/UiPath.CoreIpc.Tests/Implementation/ComputingCallback.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace UiPath.CoreIpc.Tests;
-
-public interface IComputingCallback
-{
- Task GetId(Message message);
- Task GetThreadName();
-}
-public class ComputingCallback : IComputingCallback
-{
- public string Id { get; set; }
- public async Task GetId(Message message)
- {
- message.Client.ShouldBeNull();
- return Id;
- }
-
- public async Task GetThreadName() => Thread.CurrentThread.Name;
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/Implementation/ComputingService.cs b/src/UiPath.CoreIpc.Tests/Implementation/ComputingService.cs
deleted file mode 100644
index 908ee1f0..00000000
--- a/src/UiPath.CoreIpc.Tests/Implementation/ComputingService.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-using Microsoft.Extensions.Logging;
-
-namespace UiPath.CoreIpc.Tests;
-
-public interface IInvalid : IDisposable
-{
-}
-
-public interface IDuplicateMessage
-{
- Task Test(Message message1, Message message2);
-}
-
-public interface IUploadNotification
-{
- Task Upload(Stream stream);
-}
-
-public interface IDerivedStreamDownload
-{
- Task Download();
-}
-
-public interface IDuplicateStreams
-{
- Task Upload(Stream stream, Stream stream2);
-}
-
-public interface IDerivedStreamUpload
-{
- Task Upload(MemoryStream stream);
-}
-
-public interface IMessageFirst
-{
- Task Test(Message message1, int x);
-}
-
-public interface IInvalidCancellationToken
-{
- Task Test(CancellationToken token, int x);
-}
-
-public interface IComputingServiceBase
-{
- Task AddFloat(float x, float y, CancellationToken cancellationToken = default);
-}
-public interface IComputingService : IComputingServiceBase
-{
- Task AddComplexNumber(ComplexNumber x, ComplexNumber y, CancellationToken cancellationToken = default);
- Task AddComplexNumbers(IEnumerable numbers, CancellationToken cancellationToken = default);
- Task SendMessage(SystemMessage message, CancellationToken cancellationToken = default);
- Task Infinite(CancellationToken cancellationToken = default);
- Task InfiniteVoid(CancellationToken cancellationToken = default);
- Task GetCallbackThreadName(Message message = null, CancellationToken cancellationToken = default);
-}
-
-public struct 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
-}
-
-public class ConvertTextArgs
-{
- public TextStyle TextStyle { get; set; } = TextStyle.Upper;
-
- public string Text { get; set; } = string.Empty;
-}
-
-public class ComputingService : IComputingService
-{
- private readonly ILogger _logger;
-
- public ComputingService(ILogger logger) // inject dependencies in constructor
- {
- _logger = logger;
- }
-
- public async Task AddComplexNumber(ComplexNumber x, ComplexNumber y, CancellationToken cancellationToken = default)
- {
- _logger.LogInformation($"{nameof(AddComplexNumber)} called.");
- return new ComplexNumber(x.A + y.A, x.B + y.B);
- }
-
- public async Task AddComplexNumbers(IEnumerable numbers, CancellationToken cancellationToken = default)
- {
- _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 async Task AddFloat(float x, float y, CancellationToken cancellationToken = default)
- {
- //Trace.WriteLine($"{nameof(AddFloat)} called.");
- _logger.LogInformation($"{nameof(AddFloat)} called.");
- return x + y;
- }
-
- public async Task Infinite(CancellationToken cancellationToken = default)
- {
- await Task.Delay(Timeout.Infinite, cancellationToken);
- return true;
- }
-
- public Task InfiniteVoid(CancellationToken cancellationToken = default) =>Task.Delay(Timeout.Infinite, cancellationToken);
-
- public async Task SendMessage(SystemMessage message, CancellationToken cancellationToken = default)
- {
- await Task.Delay(message.Delay, cancellationToken);
- var client = message.Client;
- var callback = message.GetCallback();
- var clientId = await callback.GetId(message);
- string returnValue = "";
- client.Impersonate(() => returnValue = client.GetUserName() + "_" + clientId + "_" + message.Text);
- return returnValue;
- }
-
- public async Task GetCallbackThreadName(Message message, CancellationToken cancellationToken = default) => await message.GetCallback().GetThreadName();
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/Implementation/IpcHelpers.cs b/src/UiPath.CoreIpc.Tests/Implementation/IpcHelpers.cs
deleted file mode 100644
index 41a9292f..00000000
--- a/src/UiPath.CoreIpc.Tests/Implementation/IpcHelpers.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using Microsoft.Extensions.Logging;
-using System.Net;
-using System.Net.WebSockets;
-using UiPath.CoreIpc.Tests;
-
-namespace UiPath.CoreIpc;
-
-public static class IpcHelpers
-{
- public static TInterface ValidateAndBuild(this ServiceClientBuilder builder) where TInterface : class where TDerived : ServiceClientBuilder
- {
-#if DEBUG
- Validator.Validate(builder);
-#endif
- return builder.Build();
- }
- public static ServiceHost ValidateAndBuild(this ServiceHostBuilder serviceHostBuilder)
- {
-#if DEBUG
- Validator.Validate(serviceHostBuilder);
-#endif
- return serviceHostBuilder.Build();
- }
- public static IServiceProvider ConfigureServices() =>
- new ServiceCollection()
- .AddLogging(b => b.AddTraceSource(new SourceSwitch("", "All")))
- .AddIpc()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .BuildServiceProvider();
- public static string GetUserName(this IClient client)
- {
- string userName = null;
- client.Impersonate(() => userName = Environment.UserName);
- return userName;
- }
- public static IServiceCollection AddIpcWithLogging(this IServiceCollection services, bool logToConsole = false)
- {
- services.AddLogging(builder =>
- {
- //if (logToConsole)
- //{
- // builder.AddConsole();
- //}
- //foreach (var listener in Trace.Listeners.Cast().Where(l => !(l is DefaultTraceListener)))
- //{
- // builder.AddTraceSource(new SourceSwitch(listener.Name, "All"), listener);
- //}
- });
- return services.AddIpc();
- }
-}
-public class HttpSysWebSocketsListener : IDisposable
-{
- HttpListener _httpListener = new();
- public HttpSysWebSocketsListener(string uriPrefix)
- {
- _httpListener.Prefixes.Add(uriPrefix);
- _httpListener.Start();
- }
- public async Task Accept(CancellationToken token)
- {
- while (true)
- {
- var listenerContext = await _httpListener.GetContextAsync();
- if (listenerContext.Request.IsWebSocketRequest)
- {
- var webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
- return webSocketContext.WebSocket;
- }
- listenerContext.Response.StatusCode = 400;
- listenerContext.Response.Close();
- }
- }
- public void Dispose() => _httpListener.Stop();
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/Implementation/OneWayStreamWrapper.cs b/src/UiPath.CoreIpc.Tests/Implementation/OneWayStreamWrapper.cs
deleted file mode 100644
index b63c41c6..00000000
--- a/src/UiPath.CoreIpc.Tests/Implementation/OneWayStreamWrapper.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) Andrew Arnott. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace UiPath.CoreIpc.Tests;
-
-internal class OneWayStreamWrapper : Stream
-{
- private readonly Stream innerStream;
- private readonly bool canRead;
- private readonly bool canWrite;
-
- internal OneWayStreamWrapper(Stream innerStream, bool canRead = false, bool canWrite = false)
- {
- if (canRead == canWrite)
- {
- throw new ArgumentException("Exactly one operation (read or write) must be true.");
- }
- this.innerStream = innerStream ?? throw new ArgumentNullException(nameof(innerStream));
- this.canRead = canRead;
- this.canWrite = canWrite;
- }
-
- public override bool CanRead => this.canRead && this.innerStream.CanRead;
-
- public override bool CanSeek => false;
-
- public override bool CanWrite => this.canWrite && this.innerStream.CanWrite;
-
- public override long Length => throw new NotSupportedException();
-
- public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
-
- public override void Flush()
- {
- if (this.CanWrite)
- {
- this.innerStream.Flush();
- }
- else
- {
- throw new NotSupportedException();
- }
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (this.CanRead)
- {
- return this.innerStream.Read(buffer, offset, count);
- }
- else
- {
- throw new NotSupportedException();
- }
- }
-
- public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (this.CanRead)
- {
- return this.innerStream.ReadAsync(buffer, offset, count, cancellationToken);
- }
- else
- {
- throw new NotSupportedException();
- }
- }
-
- public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
-
- public override void SetLength(long value) => throw new NotSupportedException();
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- if (this.CanWrite)
- {
- this.innerStream.Write(buffer, offset, count);
- }
- else
- {
- throw new NotSupportedException();
- }
- }
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (this.CanWrite)
- {
- return this.innerStream.WriteAsync(buffer, offset, count, cancellationToken);
- }
- else
- {
- throw new NotSupportedException();
- }
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- this.innerStream.Dispose();
- }
- }
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/Implementation/SystemService.cs b/src/UiPath.CoreIpc.Tests/Implementation/SystemService.cs
deleted file mode 100644
index 049f4e36..00000000
--- a/src/UiPath.CoreIpc.Tests/Implementation/SystemService.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using System.Globalization;
-using System.Text;
-
-namespace UiPath.CoreIpc.Tests;
-
-public interface ISystemService
-{
- Task DoNothing(CancellationToken cancellationToken = default);
- Task VoidThreadName(CancellationToken cancellationToken = default);
- Task VoidSyncThrow(CancellationToken cancellationToken = default);
- Task GetThreadName(CancellationToken cancellationToken = default);
- Task ConvertText(string text, TextStyle style, CancellationToken cancellationToken = default);
- Task ConvertTextWithArgs(ConvertTextArgs args, CancellationToken cancellationToken = default);
- Task GetGuid(Guid guid, CancellationToken cancellationToken = default);
- Task ReverseBytes(byte[] input, CancellationToken cancellationToken = default);
- Task SlowOperation(CancellationToken cancellationToken = default);
- Task MissingCallback(SystemMessage message, CancellationToken cancellationToken = default);
- Task Infinite(CancellationToken cancellationToken = default);
- Task ImpersonateCaller(Message message = null, CancellationToken cancellationToken = default);
- Task SendMessage(SystemMessage message, CancellationToken cancellationToken = default);
- Task Upload(Stream stream, int delay = 0, CancellationToken cancellationToken = default);
- Task Download(string text, CancellationToken cancellationToken = default);
- Task Echo(Stream input, CancellationToken cancellationToken = default);
- Task UploadNoRead(Stream memoryStream, int delay = 0, CancellationToken cancellationToken = default);
-}
-
-public class SystemMessage : Message
-{
- public string Text { get; set; }
- public int Delay { get; set; }
-}
-public class SystemService : ISystemService
-{
- public SystemService()
- {
- }
-
- public async Task Infinite(CancellationToken cancellationToken = default)
- {
- await Task.Delay(Timeout.Infinite, cancellationToken);
- return true;
- }
- public async Task ConvertTextWithArgs(ConvertTextArgs args, CancellationToken cancellationToken = default)
- => await ConvertText(args.Text, args.TextStyle, cancellationToken);
-
- public async Task ConvertText(string text, TextStyle style, CancellationToken cancellationToken = default)
- {
- switch (style)
- {
- case TextStyle.TitleCase:
- return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(text);
- case TextStyle.Upper:
- return CultureInfo.InvariantCulture.TextInfo.ToUpper(text);
- default:
- return text;
- }
- }
-
- public async Task SendMessage(SystemMessage message, CancellationToken cancellationToken = default)
- {
- var client = message.Client;
- var callback = message.GetCallback();
- var clientId = await callback.GetId(message);
- string returnValue = "";
- client.Impersonate(() => returnValue = client.GetUserName() + "_" + clientId + "_" + message.Text);
- return returnValue;
- }
-
- public bool DidNothing { get; set; }
-
- public async Task DoNothing(CancellationToken cancellationToken = default)
- {
- const int Timeout =
-#if CI
- 100;
-#else
- 10;
-#endif
- await Task.Delay(Timeout);
- DidNothing = true;
- }
-
- public async Task GetGuid(Guid guid, CancellationToken cancellationToken = default)
- {
- //throw new Exception("sssss");
- return guid;
- }
-
- public async Task ReverseBytes(byte[] input, CancellationToken cancellationToken = default)
- {
- return input.Reverse().ToArray();
- }
-
- public async Task MissingCallback(SystemMessage message, CancellationToken cancellationToken = default)
- {
- if (message.Delay != 0)
- {
- await Task.Delay(message.Delay, cancellationToken);
- }
- var domainName = "";
- var client = message.Client;
- //client.RunAs(() => domainName = "test");
- //try
- //{
- message.GetCallback();
- //}
- //catch(Exception ex)
- //{
- // Console.WriteLine(ex.ToString());
- //}
- return client.GetUserName() +" " + domainName;
- }
-
- public async Task SlowOperation(CancellationToken cancellationToken = default)
- {
- Console.WriteLine("SlowOperation " + Thread.CurrentThread.Name);
- try
- {
- for(int i = 0; i < 5; i++)
- {
- await Task.Delay(1000, cancellationToken);
- Console.WriteLine("SlowOperation "+Thread.CurrentThread.Name);
- if(cancellationToken.IsCancellationRequested)
- {
- Console.WriteLine("SlowOperation Cancelled.");
- return false;
- }
- }
- }
- catch(Exception ex)
- {
- Console.WriteLine(ex.ToString());
- }
- Console.WriteLine("SlowOperation finished. "+ (cancellationToken.IsCancellationRequested ? "cancelled " : "") + Thread.CurrentThread.Name);
- return true;
- }
-
- public string ThreadName;
-
- public Task VoidSyncThrow(CancellationToken cancellationToken = default) => throw new NotImplementedException();
-
- public async Task VoidThreadName(CancellationToken cancellationToken = default) => ThreadName = Thread.CurrentThread.Name;
-
- public async Task GetThreadName(CancellationToken cancellationToken = default) => Thread.CurrentThread.Name;
-
- public async Task ImpersonateCaller(Message message = null, CancellationToken cancellationToken = default)
- {
- var client = message.Client;
- string returnValue = "";
- client.Impersonate(() => returnValue = client.GetUserName());
- return returnValue;
- }
-
- public async Task Upload(Stream stream, int delay = 0, CancellationToken cancellationToken = default)
- {
- await Task.Delay(delay);
- return await new StreamReader(stream).ReadToEndAsync();
- }
-
- public async Task UploadNoRead(Stream stream, int delay = 0, CancellationToken cancellationToken = default)
- {
- await Task.Delay(delay);
- return "";
- }
-
- public async Task Download(string text, CancellationToken cancellationToken = default) => new MemoryStream(Encoding.UTF8.GetBytes(text));
-
- public async Task Echo(Stream input, CancellationToken cancellationToken = default)
- {
- var result = new MemoryStream();
- await input.CopyToAsync(result);
- result.Position = 0;
- return result;
- }
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/NamedPipeTests.cs b/src/UiPath.CoreIpc.Tests/NamedPipeTests.cs
deleted file mode 100644
index e81f1eaa..00000000
--- a/src/UiPath.CoreIpc.Tests/NamedPipeTests.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System.IO.Pipes;
-using System.Security.Principal;
-namespace UiPath.CoreIpc.Tests;
-
-public class SystemNamedPipeTests : SystemTests>
-{
- string _pipeName = "system";
- protected override ServiceHostBuilder Configure(ServiceHostBuilder serviceHostBuilder) =>
- serviceHostBuilder.UseNamedPipes(Configure(new NamedPipeSettings(_pipeName+GetHashCode())));
- protected override NamedPipeClientBuilder CreateSystemClientBuilder() =>
- new NamedPipeClientBuilder(_pipeName+GetHashCode()).AllowImpersonation();
- [Fact]
- public void PipeExists()
- {
- IOHelpers.PipeExists(System.Guid.NewGuid().ToString()).ShouldBeFalse();
- IOHelpers.PipeExists("system"+GetHashCode(), 50).ShouldBeTrue();
- }
- [Fact]
- public Task ServerName() => SystemClientBuilder().ValidateAndBuild().GetGuid(System.Guid.Empty);
- [Fact]
- public override void BeforeCallServerSide()
- {
- _pipeName = "beforeCall";
- base.BeforeCallServerSide();
- }
-#if WINDOWS
- [Fact]
- public async Task PipeSecurityForWindows()
- {
- _pipeName = "protected";
- using var protectedService = new ServiceHostBuilder(_serviceProvider)
- .UseNamedPipes(Configure(new NamedPipeSettings(_pipeName+GetHashCode())
- {
- AccessControl = pipeSecurity => pipeSecurity.Deny(WellKnownSidType.WorldSid, PipeAccessRights.FullControl)
- }))
- .AddEndpoint()
- .ValidateAndBuild();
- _ = protectedService.RunAsync();
- await CreateSystemService().DoNothing().ShouldThrowAsync();
- }
-#endif
-}
-public class ComputingNamedPipeTests : ComputingTests>
-{
- protected override ServiceHostBuilder Configure(ServiceHostBuilder serviceHostBuilder) =>
- serviceHostBuilder.UseNamedPipes(Configure(new NamedPipeSettings("computing" + GetHashCode())));
- protected override NamedPipeClientBuilder ComputingClientBuilder(TaskScheduler taskScheduler = null) =>
- new NamedPipeClientBuilder("computing" + GetHashCode(), _serviceProvider)
- .AllowImpersonation()
- .RequestTimeout(RequestTimeout)
- .CallbackInstance(_computingCallback)
- .TaskScheduler(taskScheduler);
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/NestedStreamTests.cs b/src/UiPath.CoreIpc.Tests/NestedStreamTests.cs
deleted file mode 100644
index b30d6f93..00000000
--- a/src/UiPath.CoreIpc.Tests/NestedStreamTests.cs
+++ /dev/null
@@ -1,370 +0,0 @@
-using System.IO.Compression;
-
-namespace UiPath.CoreIpc.Tests;
-
-public class NestedStreamTests
-{
- private const int DefaultNestedLength = 10;
-
- private MemoryStream underlyingStream;
-
- private NestedStream stream;
-
- protected static readonly TimeSpan UnexpectedTimeout = Debugger.IsAttached ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(10);
-
- private readonly CancellationTokenSource _timeoutTokenSource = new(UnexpectedTimeout);
-
- public NestedStreamTests()
- {
- var random = new Random();
- var buffer = new byte[20];
- random.NextBytes(buffer);
- this.underlyingStream = new MemoryStream(buffer);
- this.stream = this.underlyingStream.ReadSlice(DefaultNestedLength);
- }
-
- protected CancellationToken TimeoutToken => Debugger.IsAttached ? CancellationToken.None : _timeoutTokenSource.Token;
-
- [Fact]
- public void CanSeek()
- {
- Assert.True(this.stream.CanSeek);
- this.stream.Dispose();
- Assert.False(this.stream.CanSeek);
- }
-
- [Fact]
- public void CanSeek_NonSeekableStream()
- {
- using var gzipStream = new GZipStream(Stream.Null, CompressionMode.Decompress);
- using var stream = gzipStream.ReadSlice(10);
-
- Assert.False(stream.CanSeek);
- stream.Dispose();
- Assert.False(stream.CanSeek);
- }
-
- [Fact]
- public void Length()
- {
- Assert.Equal(DefaultNestedLength, this.stream.Length);
- }
-
- [Fact]
- public void Length_NonSeekableStream()
- {
- using (var gzipStream = new GZipStream(Stream.Null, CompressionMode.Decompress))
- using (var stream = gzipStream.ReadSlice(10))
- {
- stream.Length.ShouldBe(10);
- }
- }
-
- [Fact]
- public void Position()
- {
- byte[] buffer = new byte[DefaultNestedLength];
-
- Assert.Equal(0, this.stream.Position);
- var bytesRead = this.stream.Read(buffer, 0, 5);
- Assert.Equal(bytesRead, this.stream.Position);
-
- this.stream.Position = 0;
- byte[] buffer2 = new byte[DefaultNestedLength];
- bytesRead = this.stream.Read(buffer2, 0, 5);
- Assert.Equal(bytesRead, this.stream.Position);
- Assert.Equal(buffer, buffer2);
- }
-
- [Fact]
- public void Position_NonSeekableStream()
- {
- using var nonSeekableWrapper = new OneWayStreamWrapper(this.underlyingStream, canRead: true);
- using var stream = nonSeekableWrapper.ReadSlice(10);
-
- Assert.Equal(0, stream.Position);
- Assert.Throws(() => stream.Position = 3);
- Assert.Equal(0, stream.Position);
- stream.ReadByte();
- Assert.Equal(1, stream.Position);
- }
-
- [Fact]
- public void IsDisposed()
- {
- Assert.False(stream.IsDisposed);
- this.stream.Dispose();
- Assert.True(stream.IsDisposed);
- }
-
- [Fact]
- public void Dispose_IncompleteDisposesUnderylingStream()
- {
- this.stream.Dispose();
- Assert.False(this.underlyingStream.CanSeek);
- }
-
- [Fact]
- public void Dispose_DoesNotDisposeUnderylingStream()
- {
- this.stream.Read(new byte[DefaultNestedLength], 0, DefaultNestedLength);
- this.stream.Dispose();
- Assert.True(this.underlyingStream.CanSeek);
- // A sanity check that if it were disposed, our assertion above would fail.
- this.underlyingStream.Dispose();
- Assert.False(this.underlyingStream.CanSeek);
- }
-
- [Fact]
- public void SetLength()
- {
- Assert.Throws(() => this.stream.SetLength(0));
- }
-
- [Fact]
- public void Seek_Current()
- {
- Assert.Equal(0, this.stream.Position);
- Assert.Equal(0, this.stream.Seek(0, SeekOrigin.Current));
- Assert.Equal(0, this.underlyingStream.Position);
- Assert.Throws(() => this.stream.Seek(-1, SeekOrigin.Current));
- Assert.Equal(0, this.underlyingStream.Position);
-
- Assert.Equal(5, this.stream.Seek(5, SeekOrigin.Current));
- Assert.Equal(5, this.underlyingStream.Position);
- Assert.Equal(5, this.stream.Seek(0, SeekOrigin.Current));
- Assert.Equal(5, this.underlyingStream.Position);
- Assert.Equal(4, this.stream.Seek(-1, SeekOrigin.Current));
- Assert.Equal(4, this.underlyingStream.Position);
- Assert.Throws(() => this.stream.Seek(-10, SeekOrigin.Current));
- Assert.Equal(4, this.underlyingStream.Position);
-
- Assert.Equal(0, this.stream.Seek(0, SeekOrigin.Begin));
- Assert.Equal(0, this.stream.Position);
-
- Assert.Equal(DefaultNestedLength + 1, this.stream.Seek(DefaultNestedLength + 1, SeekOrigin.Current));
- Assert.Equal(DefaultNestedLength + 1, this.underlyingStream.Position);
- Assert.Equal((2 * DefaultNestedLength) + 1, this.stream.Seek(DefaultNestedLength, SeekOrigin.Current));
- Assert.Equal((2 * DefaultNestedLength) + 1, this.underlyingStream.Position);
- Assert.Equal((2 * DefaultNestedLength) + 1, this.stream.Seek(0, SeekOrigin.Current));
- Assert.Equal((2 * DefaultNestedLength) + 1, this.underlyingStream.Position);
- Assert.Equal(1, this.stream.Seek(-2 * DefaultNestedLength, SeekOrigin.Current));
- Assert.Equal(1, this.underlyingStream.Position);
-
- this.stream.Dispose();
- Assert.Throws(() => this.stream.Seek(0, SeekOrigin.Begin));
- }
-
- [Fact]
- public void Sook_WithNonStartPositionInUnderlyingStream()
- {
- this.underlyingStream.Position = 1;
- this.stream = this.underlyingStream.ReadSlice(5);
-
- Assert.Equal(0, this.stream.Position);
- Assert.Equal(2, this.stream.Seek(2, SeekOrigin.Current));
- Assert.Equal(3, this.underlyingStream.Position);
- }
-
- [Fact]
- public void Seek_Begin()
- {
- Assert.Equal(0, this.stream.Position);
- Assert.Throws(() => this.stream.Seek(-1, SeekOrigin.Begin));
- Assert.Equal(0, this.underlyingStream.Position);
-
- Assert.Equal(0, this.stream.Seek(0, SeekOrigin.Begin));
- Assert.Equal(0, this.underlyingStream.Position);
-
- Assert.Equal(5, this.stream.Seek(5, SeekOrigin.Begin));
- Assert.Equal(5, this.underlyingStream.Position);
-
- Assert.Equal(DefaultNestedLength, this.stream.Seek(DefaultNestedLength, SeekOrigin.Begin));
- Assert.Equal(DefaultNestedLength, this.underlyingStream.Position);
-
- Assert.Equal(DefaultNestedLength + 1, this.stream.Seek(DefaultNestedLength + 1, SeekOrigin.Begin));
- Assert.Equal(DefaultNestedLength + 1, this.underlyingStream.Position);
-
- this.stream.Dispose();
- Assert.Throws(() => this.stream.Seek(0, SeekOrigin.Begin));
- }
-
- [Fact]
- public void Seek_End()
- {
- Assert.Equal(0, this.stream.Position);
- Assert.Equal(9, this.stream.Seek(-1, SeekOrigin.End));
- Assert.Equal(9, this.underlyingStream.Position);
-
- Assert.Equal(DefaultNestedLength, this.stream.Seek(0, SeekOrigin.End));
- Assert.Equal(DefaultNestedLength, this.underlyingStream.Position);
-
- Assert.Equal(DefaultNestedLength + 5, this.stream.Seek(5, SeekOrigin.End));
- Assert.Equal(DefaultNestedLength + 5, this.underlyingStream.Position);
-
- Assert.Throws(() => this.stream.Seek(-20, SeekOrigin.Begin));
- Assert.Equal(DefaultNestedLength + 5, this.underlyingStream.Position);
-
- this.stream.Dispose();
- Assert.Throws(() => this.stream.Seek(0, SeekOrigin.End));
- }
-
- [Fact]
- public void Flush()
- {
- Assert.Throws(() => this.stream.Flush());
- }
-
- [Fact]
- public async Task FlushAsync()
- {
- await Assert.ThrowsAsync(() => this.stream.FlushAsync());
- }
-
- [Fact]
- public void CanRead()
- {
- Assert.True(this.stream.CanRead);
- this.stream.Dispose();
- Assert.False(this.stream.CanRead);
- }
-
- [Fact]
- public void CanWrite()
- {
- Assert.False(this.stream.CanWrite);
- this.stream.Dispose();
- Assert.False(this.stream.CanWrite);
- }
-
- [Fact]
- public async Task WriteAsync_Throws()
- {
- await Assert.ThrowsAsync(() => this.stream.WriteAsync(new byte[1], 0, 1));
- }
-
- [Fact]
- public void Write_Throws()
- {
- Assert.Throws(() => this.stream.Write(new byte[1], 0, 1));
- }
-
- [Fact]
- public async Task ReadAsync_Empty_ReturnsZero()
- {
- Assert.Equal(0, await this.stream.ReadAsync(Array.Empty(), 0, 0, default));
- }
-
- [Fact]
- public async Task Read_BeyondEndOfStream_ReturnsZero()
- {
- // Seek beyond the end of the stream
- this.stream.Seek(1, SeekOrigin.End);
-
- byte[] buffer = new byte[this.underlyingStream.Length];
-
- Assert.Equal(0, await this.stream.ReadAsync(buffer, 0, buffer.Length, this.TimeoutToken));
- }
-
- [Fact]
- public async Task ReadAsync_NoMoreThanGiven()
- {
- byte[] buffer = new byte[this.underlyingStream.Length];
- int bytesRead = await this.stream.ReadAsync(buffer, 0, buffer.Length, this.TimeoutToken);
- Assert.Equal(DefaultNestedLength, bytesRead);
-
- Assert.Equal(0, await this.stream.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead, this.TimeoutToken));
- Assert.Equal(DefaultNestedLength, this.underlyingStream.Position);
- }
-
- [Fact]
- public void Read_NoMoreThanGiven()
- {
- byte[] buffer = new byte[this.underlyingStream.Length];
- int bytesRead = this.stream.Read(buffer, 0, buffer.Length);
- Assert.Equal(DefaultNestedLength, bytesRead);
-
- Assert.Equal(0, this.stream.Read(buffer, bytesRead, buffer.Length - bytesRead));
- Assert.Equal(DefaultNestedLength, this.underlyingStream.Position);
- }
-
- [Fact]
- public void Read_Empty_ReturnsZero()
- {
- Assert.Equal(0, this.stream.Read(Array.Empty(), 0, 0));
- }
-
- [Fact]
- public async Task ReadAsync_WhenLengthIsInitially0()
- {
- this.stream = this.underlyingStream.ReadSlice(0);
- Assert.Equal(0, await this.stream.ReadAsync(new byte[1], 0, 1, this.TimeoutToken));
- }
-
- [Fact]
- public void Read_WhenLengthIsInitially0()
- {
- this.stream = this.underlyingStream.ReadSlice(0);
- Assert.Equal(0, this.stream.Read(new byte[1], 0, 1));
- }
-
- [Fact]
- public void CreationDoesNotReadFromUnderlyingStream()
- {
- Assert.Equal(0, this.underlyingStream.Position);
- }
-
- [Fact]
- public void Read_UnderlyingStreamReturnsFewerBytesThanRequested()
- {
- var buffer = new byte[20];
- int firstBlockLength = DefaultNestedLength / 2;
- this.underlyingStream.SetLength(firstBlockLength);
- Assert.Equal(firstBlockLength, this.stream.Read(buffer, 0, buffer.Length));
- this.underlyingStream.SetLength(DefaultNestedLength * 2);
- Assert.Equal(DefaultNestedLength - firstBlockLength, this.stream.Read(buffer, 0, buffer.Length));
- }
-
- [Fact]
- public async Task ReadAsync_UnderlyingStreamReturnsFewerBytesThanRequested()
- {
- var buffer = new byte[20];
- int firstBlockLength = DefaultNestedLength / 2;
- this.underlyingStream.SetLength(firstBlockLength);
- Assert.Equal(firstBlockLength, await this.stream.ReadAsync(buffer, 0, buffer.Length));
- this.underlyingStream.SetLength(DefaultNestedLength * 2);
- Assert.Equal(DefaultNestedLength - firstBlockLength, await this.stream.ReadAsync(buffer, 0, buffer.Length));
- }
-
- [Fact]
- public void Read_ValidatesArguments()
- {
- var buffer = new byte[20];
-
- Assert.Throws(() => this.stream.Read(null!, 0, 0));
- Assert.Throws(() => this.stream.Read(buffer, -1, buffer.Length));
- Assert.Throws(() => this.stream.Read(buffer, 0, -1));
- Assert.Throws(() => this.stream.Read(buffer, 1, buffer.Length));
- }
-
- [Fact]
- public async Task ReadAsync_ValidatesArguments()
- {
- var buffer = new byte[20];
-
- await Assert.ThrowsAsync(() => this.stream.ReadAsync(null!, 0, 0));
- await Assert.ThrowsAsync(() => this.stream.ReadAsync(buffer, -1, buffer.Length));
- await Assert.ThrowsAsync(() => this.stream.ReadAsync(buffer, 0, -1));
- await Assert.ThrowsAsync(() => this.stream.ReadAsync(buffer, 1, buffer.Length));
- }
-}
-public static class StreamExtensions
-{
- ///
- /// Creates a that can read no more than a given number of bytes from an underlying stream.
- ///
- /// The stream to read from.
- /// The number of bytes to read from the parent stream.
- /// A stream that ends after bytes are read.
- public static NestedStream ReadSlice(this Stream stream, long length) => new(stream, length);
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/SystemTests.cs b/src/UiPath.CoreIpc.Tests/SystemTests.cs
deleted file mode 100644
index e33832f4..00000000
--- a/src/UiPath.CoreIpc.Tests/SystemTests.cs
+++ /dev/null
@@ -1,295 +0,0 @@
-using System.Text;
-
-namespace UiPath.CoreIpc.Tests;
-
-public abstract class SystemTests : TestBase where TBuilder : ServiceClientBuilder
-{
- protected ServiceHost _systemHost;
- protected ISystemService _systemClient;
- protected readonly SystemService _systemService;
- public SystemTests()
- {
- _systemService = (SystemService)_serviceProvider.GetService();
- _systemHost = Configure(new ServiceHostBuilder(_serviceProvider))
- .AddEndpoint()
- .ValidateAndBuild();
- _systemHost.RunAsync(GuiScheduler);
- _systemClient = CreateSystemService();
- }
- protected override TSettings Configure(TSettings listenerSettings)
- {
- base.Configure(listenerSettings);
- listenerSettings.ConcurrentAccepts = 10;
- listenerSettings.RequestTimeout = RequestTimeout.Subtract(TimeSpan.FromSeconds(1));
- return listenerSettings;
- }
- public override void Dispose()
- {
- ((IDisposable)_systemClient).Dispose();
- ((IpcProxy)_systemClient).CloseConnection();
- _systemHost.Dispose();
- base.Dispose();
- }
- [Fact]
- public async Task ConcurrentRequests()
- {
- var infinite = _systemClient.Infinite();
- await Guid();
- infinite.IsCompleted.ShouldBeFalse();
- }
- [Fact]
- public async Task OptionalMessage()
- {
- var returnValue = await _systemClient.ImpersonateCaller();
- returnValue.ShouldBe(Environment.UserName);
- }
-
- [Fact]
- public async Task ServerTimeout()
- {
- var ex = _systemClient.Infinite().ShouldThrow();
- ex.Message.ShouldBe($"{nameof(_systemClient.Infinite)} timed out.");
- ex.Is().ShouldBeTrue();
- await Guid();
- }
- [Fact]
- public async Task Void()
- {
- _systemService.DidNothing = false;
- await _systemClient.DoNothing();
- _systemService.DidNothing.ShouldBeFalse();
- while (!_systemService.DidNothing)
- {
- await Task.Delay(10);
- Trace.WriteLine(this + " Void");
- }
- }
-
- [Fact]
- public async Task VoidThreadName()
- {
- await _systemClient.VoidThreadName();
- await _systemClient.GetThreadName();
- while (_systemService.ThreadName != "GuiThread")
- {
- await Task.Delay(0);
- Trace.WriteLine(this + " VoidThreadName");
- }
- }
-
- [Fact]
- public async Task Enum()
- {
- var text = await _systemClient.ConvertText("hEllO woRd!", TextStyle.Upper);
- text.ShouldBe("HELLO WORD!");
- }
-
- [Fact]
- public async Task PropertyWithTypeDefaultValue()
- {
- var args = new ConvertTextArgs { Text = "hEllO woRd!", TextStyle = default };
- var text = await _systemClient.ConvertTextWithArgs(args);
- text.ShouldBe("Hello Word!");
- }
-
- [Fact]
- public async Task MaxMessageSize()
- {
- _systemClient.ReverseBytes(new byte[MaxReceivedMessageSizeInMegabytes * 1024 * 1024]).ShouldThrow();
- await Guid();
- }
-
- [Fact]
- public async Task Guid()
- {
- var newGuid = System.Guid.NewGuid();
- var guid = await _systemClient.GetGuid(newGuid);
- guid.ShouldBe(newGuid);
- }
-
- [Fact]
- public Task LargeMessage() => _systemClient.ReverseBytes(new byte[(int)(0.7 * MaxReceivedMessageSizeInMegabytes * 1024 * 1024)]);
-
- [Fact]
- public async Task ReverseBytes()
- {
- var input = Encoding.UTF8.GetBytes("Test");
- var reversed = await _systemClient.ReverseBytes(input);
- reversed.ShouldBe(input.Reverse());
- }
-
- [Fact]
- public async Task MissingCallback()
- {
- RemoteException exception = null;
- try
- {
- await _systemClient.MissingCallback(new SystemMessage());
- }
- catch (RemoteException ex)
- {
- exception = ex;
- }
- exception.Message.ShouldBe("Callback contract mismatch. Requested System.IDisposable, but it's not configured.");
- exception.Is().ShouldBeTrue();
- await Guid();
- }
-
-
- [Fact]
- public async Task VoidIsAsync() => await _systemClient.VoidSyncThrow();
-
- [Fact]
- public async Task GetThreadName() => (await _systemClient.GetThreadName()).ShouldBe("GuiThread");
-
- [Fact]
- public async Task Echo()
- {
- using var stream = await _systemClient.Echo(new MemoryStream(Encoding.UTF8.GetBytes("Hello world")));
- (await new StreamReader(stream).ReadToEndAsync()).ShouldBe("Hello world");
- }
-
- [Fact]
- public async Task CancelUpload()
- {
- var stream = new MemoryStream(Enumerable.Range(1, 50000).Select(i=>(byte)i).ToArray());
- await _systemClient.GetThreadName();
- using (var cancellationSource = new CancellationTokenSource(5))
- {
- _systemClient.Upload(stream, 20, cancellationSource.Token).ShouldThrow();
- }
- }
-
- [Fact]
- public async Task Upload()
- {
- (await _systemClient.Upload(new MemoryStream(Encoding.UTF8.GetBytes("Hello world")))).ShouldBe("Hello world");
- await Guid();
- }
-
- [Fact]
- public virtual async Task UploadNoRead()
- {
- try
- {
- (await _systemClient.UploadNoRead(new MemoryStream(Encoding.UTF8.GetBytes("Hello world")))).ShouldBeEmpty();
- }
- catch (IOException) { }
- catch (ObjectDisposedException) { }
- await Guid();
- }
-
- [Fact]
- public Task DownloadUiThread() => Task.Factory.StartNew(Download, default, TaskCreationOptions.DenyChildAttach, GuiScheduler).Unwrap();
- [Fact]
- public async Task Download()
- {
- using var stream = await _systemClient.Download("Hello world");
- (await new StreamReader(stream).ReadToEndAsync()).ShouldBe("Hello world");
- }
- [Fact]
- public async Task DownloadNoRead()
- {
- using (await _systemClient.Download("Hello world")) { }
- await Guid();
- }
- protected abstract TBuilder CreateSystemClientBuilder();
- protected TBuilder SystemClientBuilder() => CreateSystemClientBuilder().SerializeParametersAsObjects().RequestTimeout(RequestTimeout).Logger(_serviceProvider);
- [Fact]
- public async Task BeforeCall()
- {
- bool newConnection = false;
- var proxy = SystemClientBuilder().BeforeCall(async (c, _) =>
- {
- newConnection = c.NewConnection;
- c.Method.ShouldBe(typeof(ISystemService).GetMethod(nameof(ISystemService.DoNothing)));
- c.Arguments.Single().ShouldBe(""); // cancellation token
- }).ValidateAndBuild();
- newConnection.ShouldBeFalse();
-
- await proxy.DoNothing();
- newConnection.ShouldBeTrue();
-
- await proxy.DoNothing();
- newConnection.ShouldBeFalse();
- var ipcProxy = (IpcProxy)proxy;
- var closed = false;
- ipcProxy.Connection.Closed += delegate { closed = true; };
- ipcProxy.CloseConnection();
- closed.ShouldBeTrue();
- newConnection.ShouldBeFalse();
- await proxy.DoNothing();
- newConnection.ShouldBeTrue();
-
- await proxy.DoNothing();
- newConnection.ShouldBeFalse();
- ipcProxy.CloseConnection();
- }
-
- [Fact]
- public async Task DontReconnect()
- {
- var proxy = SystemClientBuilder().DontReconnect().ValidateAndBuild();
- await proxy.GetGuid(System.Guid.Empty);
- ((IpcProxy)proxy).CloseConnection();
- ObjectDisposedException exception = null;
- try
- {
- await proxy.GetGuid(System.Guid.Empty);
- }
- catch (ObjectDisposedException ex)
- {
- exception = ex;
- }
- exception.ShouldNotBeNull();
- }
- [Fact]
- public Task CancelServerCall() => CancelServerCallCore(10);
- protected ISystemService CreateSystemService() => SystemClientBuilder().ValidateAndBuild();
-
- async Task CancelServerCallCore(int counter)
- {
- for (int i = 0; i < counter; i++)
- {
- var request = new SystemMessage { RequestTimeout = Timeout.InfiniteTimeSpan, Delay = Timeout.Infinite };
- var sendMessageResult = _systemClient.MissingCallback(request);
- var newGuid = System.Guid.NewGuid();
- (await _systemClient.GetGuid(newGuid)).ShouldBe(newGuid);
- await Task.Delay(1);
- ((IpcProxy)_systemClient).CloseConnection();
- sendMessageResult.ShouldThrow();
- newGuid = System.Guid.NewGuid();
- (await _systemClient.GetGuid(newGuid)).ShouldBe(newGuid);
- }
- }
- [Fact]
- public async Task ClosingTheHostShouldCloseTheConnection()
- {
- var request = new SystemMessage { RequestTimeout = Timeout.InfiniteTimeSpan, Delay = Timeout.Infinite };
- var sendMessageResult = _systemClient.MissingCallback(request);
- var newGuid = System.Guid.NewGuid();
- (await _systemClient.GetGuid(newGuid)).ShouldBe(newGuid);
- await Task.Delay(1);
- _systemHost.Dispose();
- sendMessageResult.ShouldThrow();
- }
- [Fact]
- public virtual async void BeforeCallServerSide()
- {
- var newGuid = System.Guid.NewGuid();
- MethodInfo method = null;
- using var protectedService = Configure(new ServiceHostBuilder(_serviceProvider))
- .AddEndpoint(new EndpointSettings
- {
- BeforeCall = async (call, ct) =>
- {
- method = call.Method;
- call.Arguments[0].ShouldBe(newGuid);
- }
- })
- .ValidateAndBuild();
- _ = protectedService.RunAsync();
- await CreateSystemService().GetGuid(newGuid);
- method.ShouldBe(typeof(ISystemService).GetMethod(nameof(ISystemService.GetGuid)));
- }
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/TcpTests..cs b/src/UiPath.CoreIpc.Tests/TcpTests..cs
deleted file mode 100644
index cca919c5..00000000
--- a/src/UiPath.CoreIpc.Tests/TcpTests..cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Net;
-using UiPath.CoreIpc.Tcp;
-namespace UiPath.CoreIpc.Tests;
-public class SystemTcpTests : SystemTests>
-{
- int _port = 3131 + GetCount();
- protected override ServiceHostBuilder Configure(ServiceHostBuilder serviceHostBuilder) =>
- serviceHostBuilder.UseTcp(Configure(new TcpSettings(GetEndPoint())));
- protected override TcpClientBuilder CreateSystemClientBuilder() => new(GetEndPoint());
- [Fact]
- public override async void BeforeCallServerSide()
- {
- _port++;
- base.BeforeCallServerSide();
- }
- IPEndPoint GetEndPoint() => new(IPAddress.Loopback, _port);
-}
-public class ComputingTcpTests : ComputingTests>
-{
- protected static readonly IPEndPoint ComputingEndPoint = new(IPAddress.Loopback, 2121+GetCount());
- protected override TcpClientBuilder ComputingClientBuilder(TaskScheduler taskScheduler = null) =>
- new TcpClientBuilder(ComputingEndPoint, _serviceProvider)
- .RequestTimeout(RequestTimeout)
- .CallbackInstance(_computingCallback)
- .TaskScheduler(taskScheduler);
- protected override ServiceHostBuilder Configure(ServiceHostBuilder serviceHostBuilder) =>
- serviceHostBuilder.UseTcp(Configure(new TcpSettings(ComputingEndPoint)));
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/TestBase.cs b/src/UiPath.CoreIpc.Tests/TestBase.cs
deleted file mode 100644
index 98d75a4e..00000000
--- a/src/UiPath.CoreIpc.Tests/TestBase.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Nito.AsyncEx;
-
-namespace UiPath.CoreIpc.Tests;
-
-public abstract class TestBase : IDisposable
-{
- protected const int MaxReceivedMessageSizeInMegabytes = 1;
- protected static int Count = -1;
- public static readonly TimeSpan RequestTimeout =
-#if CI
- TimeSpan.FromSeconds(2) +
-#endif
- (Debugger.IsAttached ? TimeSpan.FromDays(1) : TimeSpan.FromSeconds(2));
- protected readonly IServiceProvider _serviceProvider;
- protected readonly AsyncContext _guiThread = new AsyncContextThread().Context;
-
- //static TestBase()
- //{
- // AppContext.SetSwitch("Switch.System.Net.DontEnableSystemDefaultTlsVersions", false);
- //}
- public TestBase()
- {
- _guiThread.SynchronizationContext.Send(() => Thread.CurrentThread.Name = "GuiThread");
- _serviceProvider = IpcHelpers.ConfigureServices();
- }
-
- protected static int GetCount() => Interlocked.Increment(ref Count);
-
- protected TaskScheduler GuiScheduler => _guiThread.Scheduler;
-
- public virtual void Dispose() => _guiThread.Dispose();
- protected virtual TSettings Configure(TSettings listenerSettings) where TSettings : ListenerSettings
- {
- listenerSettings.RequestTimeout = RequestTimeout;
- listenerSettings.MaxReceivedMessageSizeInMegabytes = MaxReceivedMessageSizeInMegabytes;
- return listenerSettings;
- }
- protected abstract ServiceHostBuilder Configure(ServiceHostBuilder serviceHostBuilder);
-}
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/UiPath.CoreIpc.Tests.csproj b/src/UiPath.CoreIpc.Tests/UiPath.CoreIpc.Tests.csproj
deleted file mode 100644
index eaf03da6..00000000
--- a/src/UiPath.CoreIpc.Tests/UiPath.CoreIpc.Tests.csproj
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- net6.0;net461;net6.0-windows
- $(NoWarn);1998
- $(DefineConstants);$(DefineConstantsEx)
- latest
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/UiPath.CoreIpc.Tests/ValidationTests.cs b/src/UiPath.CoreIpc.Tests/ValidationTests.cs
deleted file mode 100644
index a1a0197c..00000000
--- a/src/UiPath.CoreIpc.Tests/ValidationTests.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-namespace UiPath.CoreIpc.Tests;
-
-public class ValidationTests
-{
- class JobFailedException : Exception
- {
- public JobFailedException(Error error) : base("Job has failed.", new RemoteException(error))
- {
- }
- }
-
- [Fact]
- public void ErrorFromRemoteException()
- {
- var innerError = new InvalidDataException("invalid").ToError();
- var error = new JobFailedException(innerError).ToError();
- error.Type.ShouldBe(typeof(JobFailedException).FullName);
- error.Message.ShouldBe("Job has failed.");
- error.InnerError.Type.ShouldBe(typeof(InvalidDataException).FullName);
- error.InnerError.Message.ShouldBe("invalid");
- }
- [Fact]
- public void SerializeDefaultValueToString() => new IpcJsonSerializer().Serialize(new Message(0)).ShouldBe("{\"Payload\":0}");
- [Fact]
- public void SerializeNullToString() => new IpcJsonSerializer().Serialize(new Message(null)).ShouldBe("{\"Payload\":null}");
-#if DEBUG
- [Fact]
- public void MethodsMustReturnTask() => new Action(() => new NamedPipeClientBuilder("").ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("Method does not return Task!");
- [Fact]
- public void DuplicateMessageParameters() => new Action(() => new NamedPipeClientBuilder("").ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("The message must be the last parameter before the cancellation token!");
- [Fact]
- public void TheMessageMustBeTheLastBeforeTheToken() => new Action(() => new NamedPipeClientBuilder("").ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("The message must be the last parameter before the cancellation token!");
- [Fact]
- public void CancellationTokenMustBeLast() => new Action(() => new NamedPipeClientBuilder("").ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("The CancellationToken parameter must be the last!");
- [Fact]
- public void UploadMustReturn() => new Action(() => new NamedPipeClientBuilder("").ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("Upload methods must return a value!");
- [Fact]
- public void DuplicateStreams() => new Action(() => new NamedPipeClientBuilder("").ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("Only one Stream parameter is allowed!");
- [Fact]
- public void UploadDerivedStream() => new Action(() => new NamedPipeClientBuilder("").ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("Stream parameters must be typed as Stream!");
- [Fact]
- public void DownloadDerivedStream() => new Action(() => new NamedPipeClientBuilder("").ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("Stream parameters must be typed as Stream!");
- [Fact]
- public void TheCallbackContractMustBeAnInterface() => new Action(() => new NamedPipeClientBuilder("", IpcHelpers.ConfigureServices()).ValidateAndBuild()).ShouldThrow().Message.ShouldStartWith("The contract must be an interface!");
- [Fact]
- public void TheServiceContractMustBeAnInterface() => new Action(() => new ServiceHostBuilder(IpcHelpers.ConfigureServices()).AddEndpoint