Skip to content

Commit 28c6d3c

Browse files
simplify Ipc client api
1 parent b5ce48c commit 28c6d3c

27 files changed

+390
-262
lines changed

src/Clients/js/dotnet/UiPath.CoreIpc.NodeInterop/Program.cs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,30 +120,42 @@ IEnumerable<Task> EnumeratePings()
120120
{
121121
if (webSocketUrl is not null)
122122
{
123-
yield return new WebSocketClient()
123+
yield return new IpcClient
124124
{
125-
Uri = new(webSocketUrl),
126-
ServiceProvider = sp,
127-
RequestTimeout = TimeSpan.FromHours(5),
128-
Callbacks = new()
125+
Config = new()
129126
{
130-
{ typeof(IArithmetic), callback }
127+
ServiceProvider = sp,
128+
RequestTimeout = TimeSpan.FromHours(5),
129+
Callbacks = new()
130+
{
131+
{ typeof(IArithmetic), callback }
132+
},
131133
},
134+
Transport = new WebSocketTransport
135+
{
136+
Uri = new(webSocketUrl),
137+
}
132138
}
133139
.GetProxy<IAlgebra>()
134140
.Ping();
135141
}
136142

137143
if (pipeName is not null)
138144
{
139-
yield return new NamedPipeClient()
145+
yield return new IpcClient
140146
{
141-
PipeName = pipeName,
142-
ServiceProvider = sp,
143-
RequestTimeout = TimeSpan.FromHours(5),
144-
Callbacks = new()
147+
Config = new()
148+
{
149+
ServiceProvider = sp,
150+
RequestTimeout = TimeSpan.FromHours(5),
151+
Callbacks = new()
152+
{
153+
{ typeof(IArithmetic), callback }
154+
}
155+
},
156+
Transport = new NamedPipeTransport()
145157
{
146-
{ typeof(IArithmetic), callback }
158+
PipeName = pipeName,
147159
}
148160
}
149161
.GetProxy<IAlgebra>()

src/Playground/Program.cs

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,14 @@ private static async Task Main(string[] args)
4040
ServiceProvider = serverSP,
4141
Endpoints = new()
4242
{
43-
typeof(Contracts.IServerOperations),
43+
typeof(Contracts.IServerOperations), // DEVINE
44+
new EndpointSettings(typeof(Contracts.IServerOperations)) // ASTALALT
45+
{
46+
BeforeCall = async (callInfo, _) =>
47+
{
48+
Console.WriteLine($"Server: {callInfo.Method.Name}");
49+
}
50+
},
4451
typeof(Contracts.IClientOperations2)
4552
},
4653
Listeners = [
@@ -73,21 +80,66 @@ private static async Task Main(string[] args)
7380
throw;
7481
}
7582

76-
var proxy1 = new NamedPipeClient()
83+
var c1 = new IpcClient()
7784
{
78-
PipeName = Contracts.PipeName,
79-
ServerName = ".",
80-
AllowImpersonation = false,
85+
Config = new()
86+
{
87+
Callbacks = new()
88+
{
89+
typeof(Contracts.IClientOperations),
90+
{ typeof(Contracts.IClientOperations2), new Impl.Client2() },
91+
},
92+
ServiceProvider = clientSP,
93+
Scheduler = clientScheduler,
94+
},
95+
Transport = new NamedPipeTransport()
96+
{
97+
PipeName = Contracts.PipeName,
98+
ServerName = ".",
99+
AllowImpersonation = false,
100+
},
101+
};
102+
103+
var c2 = new IpcClient()
104+
{
105+
Config = new()
106+
{
107+
ServiceProvider = clientSP,
108+
Callbacks = new()
109+
{
110+
typeof(Contracts.IClientOperations),
111+
{ typeof(Contracts.IClientOperations2), new Impl.Client2() },
112+
},
113+
Scheduler = clientScheduler,
114+
},
115+
Transport = new NamedPipeTransport()
116+
{
117+
PipeName = Contracts.PipeName,
118+
ServerName = ".",
119+
AllowImpersonation = false,
120+
},
121+
};
81122

82-
ServiceProvider = clientSP,
83-
Callbacks = new()
123+
var proxy1 = new IpcClient()
124+
{
125+
Config = new()
84126
{
85-
typeof(Contracts.IClientOperations),
86-
{ typeof(Contracts.IClientOperations2), new Impl.Client2() }
127+
ServiceProvider = clientSP,
128+
Callbacks = new()
129+
{
130+
typeof(Contracts.IClientOperations),
131+
{ typeof(Contracts.IClientOperations2), new Impl.Client2() },
132+
},
133+
Scheduler = clientScheduler,
87134
},
88-
Scheduler = clientScheduler
89-
}
90-
.GetProxy<Contracts.IServerOperations>();
135+
Transport = new NamedPipeTransport()
136+
{
137+
PipeName = Contracts.PipeName,
138+
ServerName = ".",
139+
AllowImpersonation = false,
140+
},
141+
}.GetProxy<Contracts.IServerOperations>();
142+
91143

92144
await proxy1.Register();
93145
await proxy1.Broadcast("Hello Bidirectional Http!");
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace UiPath.Ipc;
2+
3+
public class IpcProxy : DispatchProxy, IDisposable
4+
{
5+
internal ServiceClient ServiceClient { get; set; } = null!;
6+
7+
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
8+
=> ServiceClient.Invoke(targetMethod!, args!);
9+
10+
public void Dispose() => ServiceClient?.Dispose();
11+
12+
public ValueTask CloseConnection() => ServiceClient.CloseConnection();
13+
14+
public event EventHandler ConnectionClosed
15+
{
16+
add => ServiceClient.ConnectionClosed += value;
17+
remove => ServiceClient.ConnectionClosed -= value;
18+
}
19+
20+
public Stream? Network => ServiceClient.Network;
21+
}

src/UiPath.CoreIpc/Client/ServiceClient.cs

Lines changed: 12 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
namespace UiPath.Ipc;
22

3-
using System.Linq.Expressions;
4-
using ServiceClientProperFactory = Func<ClientBase, Type, ServiceClient>;
5-
63
internal abstract class ServiceClient : IDisposable
74
{
85
#region " NonGeneric-Generic adapter cache "
@@ -19,7 +16,6 @@ private static InvokeDelegate CreateInvokeDelegate(Type returnType)
1916
#endregion
2017

2118
protected abstract TimeSpan RequestTimeout { get; }
22-
protected abstract ConnectionFactory? ConnectionFactory { get; }
2319
protected abstract BeforeCallHandler? BeforeCall { get; }
2420
protected abstract ILogger? Log { get; }
2521
protected abstract string DebugName { get; }
@@ -151,47 +147,11 @@ private void Dispose(bool disposing)
151147
public override string ToString() => DebugName;
152148
}
153149

154-
internal static class ServiceClientProper
155-
{
156-
private static readonly ConcurrentDictionary<Type, ServiceClientProperFactory> CachedFactories = new();
157-
private static ServiceClientProperFactory GetFactory(Type clientType) => CachedFactories.GetOrAdd(clientType, CreateFactory);
158-
private static ServiceClientProperFactory CreateFactory(Type clientType)
159-
{
160-
if (clientType
161-
.GetInterfaces()
162-
.SingleOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IClient<,>))
163-
?.GetGenericArguments()
164-
is not [var clientStateType, var clientType2] || clientType2 != clientType)
165-
{
166-
throw new ArgumentOutOfRangeException(nameof(clientType), "The client implements 0 or more than 1 IClient<,> interfaces or the single interface's 2nd generic argument is not the client type itself.");
167-
}
168-
169-
var ctor = typeof(ServiceClientProper<,>)
170-
.MakeGenericType(clientType, clientStateType)
171-
.GetConstructor([clientType, typeof(Type)])!;
172-
173-
var paramofClientBase = Expression.Parameter(typeof(ClientBase));
174-
var paramofType = Expression.Parameter(typeof(Type));
175-
return Expression.Lambda<ServiceClientProperFactory>(
176-
Expression.New(
177-
ctor,
178-
Expression.Convert(paramofClientBase, clientType),
179-
paramofType),
180-
paramofClientBase,
181-
paramofType).Compile();
182-
}
183-
184-
public static ServiceClient Create(ClientBase client, Type proxyType)
185-
=> GetFactory(client.GetType())(client, proxyType);
186-
}
187-
188-
internal sealed class ServiceClientProper<TClient, TClientState> : ServiceClient
189-
where TClient : ClientBase, IClient<TClientState, TClient>
190-
where TClientState : class, IClientState<TClient, TClientState>, new()
150+
internal sealed class ServiceClientProper : ServiceClient
191151
{
192152
private readonly FastAsyncLock _lock = new();
193-
private readonly TClientState _clientState = new();
194-
private readonly TClient _client;
153+
private readonly IpcClient _client;
154+
private readonly IClientState _clientState;
195155

196156
private Connection? _latestConnection;
197157
private Server? _latestServer;
@@ -222,9 +182,10 @@ private Connection? LatestConnection
222182

223183
public override Stream? Network => LatestConnection?.Network;
224184

225-
public ServiceClientProper(TClient client, Type interfaceType) : base(interfaceType)
185+
public ServiceClientProper(IpcClient client, Type interfaceType) : base(interfaceType)
226186
{
227187
_client = client;
188+
_clientState = client.Transport.CreateState();
228189
}
229190

230191
public override async ValueTask CloseConnection()
@@ -248,21 +209,15 @@ public override async ValueTask CloseConnection()
248209
}
249210

250211
LatestConnection = new Connection(await Connect(ct), Serializer, Log, DebugName);
251-
var router = new Router(_client.CreateCallbackRouterConfig(), _client.ServiceProvider);
252-
_latestServer = new Server(router, _client.RequestTimeout, LatestConnection);
212+
var router = new Router(_client.Config.CreateCallbackRouterConfig(), _client.Config.ServiceProvider);
213+
_latestServer = new Server(router, _client.Config.RequestTimeout, LatestConnection);
253214
LatestConnection.Listen().LogException(Log, DebugName);
254215
return (LatestConnection, newlyConnected: true);
255216
}
256217
}
257218

258219
private async Task<Network> Connect(CancellationToken ct)
259220
{
260-
if (ConnectionFactory is not null
261-
&& await ConnectionFactory(_clientState.Network, ct) is { } userProvidedNetwork)
262-
{
263-
return userProvidedNetwork;
264-
}
265-
266221
await _clientState.Connect(_client, ct);
267222

268223
if (_clientState.Network is not { } network)
@@ -273,12 +228,11 @@ private async Task<Network> Connect(CancellationToken ct)
273228
return network;
274229
}
275230

276-
protected override TimeSpan RequestTimeout => _client.RequestTimeout;
277-
protected override ConnectionFactory? ConnectionFactory => _client.ConnectionFactory;
278-
protected override BeforeCallHandler? BeforeCall => _client.BeforeCall;
279-
protected override ILogger? Log => _client.Logger;
280-
protected override string DebugName => _client.ToString();
281-
protected override ISerializer? Serializer => _client.Serializer;
231+
protected override TimeSpan RequestTimeout => _client.Config.RequestTimeout;
232+
protected override BeforeCallHandler? BeforeCall => _client.Config.BeforeCall;
233+
protected override ILogger? Log => _client.Config.Logger;
234+
protected override string DebugName => _client.Transport.ToString();
235+
protected override ISerializer? Serializer => _client.Config.Serializer;
282236
}
283237

284238
internal sealed class ServiceClientForCallback : ServiceClient
@@ -298,29 +252,8 @@ public ServiceClientForCallback(Connection connection, Listener listener, Type i
298252
=> Task.FromResult((_connection, newlyConnected: false));
299253

300254
protected override TimeSpan RequestTimeout => _listener.Config.RequestTimeout;
301-
protected override ConnectionFactory? ConnectionFactory => null;
302255
protected override BeforeCallHandler? BeforeCall => null;
303256
protected override ILogger? Log => null;
304257
protected override string DebugName => $"ReverseClient for {_listener}";
305258
protected override ISerializer? Serializer => null;
306259
}
307-
308-
public class IpcProxy : DispatchProxy, IDisposable
309-
{
310-
internal ServiceClient ServiceClient { get; set; } = null!;
311-
312-
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
313-
=> ServiceClient.Invoke(targetMethod!, args!);
314-
315-
public void Dispose() => ServiceClient?.Dispose();
316-
317-
public ValueTask CloseConnection() => ServiceClient.CloseConnection();
318-
319-
public event EventHandler ConnectionClosed
320-
{
321-
add => ServiceClient.ConnectionClosed += value;
322-
remove => ServiceClient.ConnectionClosed -= value;
323-
}
324-
325-
public Stream? Network => ServiceClient.Network;
326-
}
Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,23 @@
11
namespace UiPath.Ipc;
22

3-
public abstract record ClientBase : EndpointConfig
3+
public sealed record ClientConfig : EndpointConfig
44
{
5-
private readonly ConcurrentDictionary<Type, ServiceClient> _clients = new();
6-
private ServiceClient GetServiceClient(Type proxyType) => _clients.GetOrAdd(proxyType, ServiceClientProper.Create(this, proxyType));
5+
public EndpointCollection? Callbacks { get; init; }
76

87
public IServiceProvider? ServiceProvider { get; init; }
9-
public EndpointCollection? Callbacks { get; init; }
108
public ILogger? Logger { get; init; }
11-
public ConnectionFactory? ConnectionFactory { get; init; }
129
public BeforeCallHandler? BeforeCall { get; init; }
1310
public TaskScheduler? Scheduler { get; init; }
1411
public ISerializer? Serializer { get; set; }
1512

16-
public virtual void Validate() { }
17-
18-
public TProxy GetProxy<TProxy>() where TProxy : class
19-
=> GetServiceClient(typeof(TProxy)).GetProxy<TProxy>();
20-
21-
internal void ValidateInternal()
13+
internal void Validate()
2214
{
2315
var haveDeferredInjectedCallbacks = Callbacks?.Any(x => x.Service.MaybeGetServiceProvider() is null && x.Service.MaybeGetInstance() is null) ?? false;
2416

2517
if (haveDeferredInjectedCallbacks && ServiceProvider is null)
2618
{
2719
throw new InvalidOperationException("ServiceProvider is required when you register injectable callbacks. Consider registering a callback instance.");
2820
}
29-
30-
Validate();
3121
}
3222

3323
internal ILogger? GetLogger(string name)
@@ -55,16 +45,10 @@ internal override RouterConfig CreateCallbackRouterConfig()
5545
});
5646
}
5747

58-
public interface IClient<TState, TSelf>
59-
where TSelf : ClientBase, IClient<TState, TSelf>
60-
where TState : class, IClientState<TSelf, TState>, new() { }
61-
62-
public interface IClientState<TClient, TSelf> : IDisposable
63-
where TSelf : class, IClientState<TClient, TSelf>, new()
64-
where TClient : ClientBase, IClient<TSelf, TClient>
48+
public interface IClientState : IDisposable
6549
{
6650
Network? Network { get; }
6751

6852
bool IsConnected();
69-
ValueTask Connect(TClient client, CancellationToken ct);
53+
ValueTask Connect(IpcClient client, CancellationToken ct);
7054
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace UiPath.Ipc;
2+
3+
public abstract record ClientTransport
4+
{
5+
public abstract IClientState CreateState();
6+
public abstract void Validate();
7+
}

0 commit comments

Comments
 (0)