Skip to content

Commit

Permalink
decommission object params (#103)
Browse files Browse the repository at this point in the history
* decommission object params
* rely on BuildNumber for the VersionSuffix
  • Loading branch information
eduard-dumitru authored Jun 26, 2024
1 parent a25cd01 commit 22df014
Show file tree
Hide file tree
Showing 21 changed files with 46 additions and 95 deletions.
4 changes: 2 additions & 2 deletions src/CI/azp-initialization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ steps:
env:
DotNet_MainProjectPath: $(DotNet_MainProjectPath)
# If $(PublishRelease) != "true" then compute $(FullVersion) as $(Version)-20210811-12-fix
# If $(PublishRelease) != "true" then compute $(FullVersion) as $(Version)-$(Build.BuildNumber)
- task: VariableTransformTask@1
displayName: '$(Label_Initialization) Compute $[FullVersion] when $[PublishRelease] is not true'
inputs:
value: '$(Version)-20210811-12-fix'
value: '$(Version)-$(Build.BuildNumber)'
variableName: 'FullVersion'
IsSecret: false
transformAction: 'none'
Expand Down
3 changes: 1 addition & 2 deletions src/IpcSample.ConsoleClient/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,14 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
var serviceProvider = ConfigureServices();
var callback = new ComputingCallback { Id = "custom made" };
var computingClientBuilder = new NamedPipeClientBuilder<IComputingService, IComputingCallback>("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
{
var computingClient = computingClientBuilder.ValidateAndBuild();
var systemClient =
new NamedPipeClientBuilder<ISystemService>("test")
.SerializeParametersAsObjects()
.RequestTimeout(TimeSpan.FromSeconds(2))
.Logger(serviceProvider)
.AllowImpersonation()
Expand Down
3 changes: 1 addition & 2 deletions src/IpcSample.ConsoleClient/TcpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,14 @@ private static async Task RunTestsAsync(CancellationToken cancellationToken)
var serviceProvider = ConfigureServices();
var callback = new ComputingCallback { Id = "custom made" };
var computingClientBuilder = new TcpClientBuilder<IComputingService, IComputingCallback>(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
{
var computingClient = computingClientBuilder.ValidateAndBuild();
var systemClient =
new TcpClientBuilder<ISystemService>(SystemEndPoint)
.SerializeParametersAsObjects()
//.EncryptAndSign("localhost")
.RequestTimeout(TimeSpan.FromSeconds(2))
.Logger(serviceProvider)
Expand Down
4 changes: 2 additions & 2 deletions src/IpcSample.ConsoleClient/WebSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ 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<IComputingService, IComputingCallback>(uri, serviceProvider).SerializeParametersAsObjects()
var computingClientBuilder = new WebSocketClientBuilder<IComputingService, IComputingCallback>(uri, serviceProvider)
.CallbackInstance(callback)/*.EncryptAndSign("localhost")*/.RequestTimeout(TimeSpan.FromSeconds(2));
var stopwatch = Stopwatch.StartNew();
int count = 0;
try
{
var computingClient = computingClientBuilder.ValidateAndBuild();
var systemClient =
new WebSocketClientBuilder<ISystemService>(uri).SerializeParametersAsObjects()
new WebSocketClientBuilder<ISystemService>(uri)
//.EncryptAndSign("localhost")
.RequestTimeout(TimeSpan.FromSeconds(2))
.Logger(serviceProvider)
Expand Down
4 changes: 2 additions & 2 deletions src/UiPath.CoreIpc.Tests/ComputingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public ComputingTests()
.AddEndpoint<IComputingService, IComputingCallback>()
.ValidateAndBuild();
_computingHost.RunAsync(GuiScheduler);
_computingClient = ComputingClientBuilder(GuiScheduler).SerializeParametersAsObjects().ValidateAndBuild();
_computingClient = ComputingClientBuilder(GuiScheduler).ValidateAndBuild();
}
protected abstract TBuilder ComputingClientBuilder(TaskScheduler taskScheduler = null);
[Fact]
Expand Down Expand Up @@ -58,7 +58,7 @@ public async Task ClientCancellation()
[Fact]
public async Task ClientTimeout()
{
var proxy = ComputingClientBuilder().SerializeParametersAsObjects().RequestTimeout(TimeSpan.FromMilliseconds(10)).ValidateAndBuild();
var proxy = ComputingClientBuilder().RequestTimeout(TimeSpan.FromMilliseconds(10)).ValidateAndBuild();
proxy.Infinite().ShouldThrow<TimeoutException>().Message.ShouldBe($"{nameof(_computingClient.Infinite)} timed out.");
await proxy.GetCallbackThreadName(new Message { RequestTimeout = RequestTimeout });
((IDisposable)proxy).Dispose();
Expand Down
4 changes: 1 addition & 3 deletions src/UiPath.CoreIpc.Tests/EndpointTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,11 @@ private NamedPipeClientBuilder<IComputingService, IComputingCallback> ComputingC
.AllowImpersonation()
.RequestTimeout(RequestTimeout)
.CallbackInstance(_computingCallback)
.SerializeParametersAsObjects()
.TaskScheduler(taskScheduler);
private ISystemService CreateSystemService() => SystemClientBuilder().ValidateAndBuild();
private NamedPipeClientBuilder<ISystemService, ISystemCallback> SystemClientBuilder() =>
new NamedPipeClientBuilder<ISystemService, ISystemCallback>(PipeName, _serviceProvider)
.CallbackInstance(_systemCallback)
.SerializeParametersAsObjects()
.RequestTimeout(RequestTimeout)
.AllowImpersonation();
public void Dispose()
Expand All @@ -66,7 +64,7 @@ public async Task Callback()
private async Task CallbackCore()
{
var proxy = new NamedPipeClientBuilder<IComputingServiceBase>(PipeName)
.SerializeParametersAsObjects().RequestTimeout(RequestTimeout).AllowImpersonation().ValidateAndBuild();
.RequestTimeout(RequestTimeout).AllowImpersonation().ValidateAndBuild();
var message = new SystemMessage { Text = Guid.NewGuid().ToString() };
var computingTask = _computingClient.SendMessage(message);
var systemTask = _systemClient.SendMessage(message);
Expand Down
7 changes: 6 additions & 1 deletion src/UiPath.CoreIpc.Tests/NamedPipeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,16 @@ public async Task PipeSecurityForWindows()
[InlineData(10, 10, 10, 10)]
public async Task PipeCancelIoOnServer_AnyNoOfTimes(params int[] msDelays)
{
bool cancelIoResult = false;

// Any number of kernel32.CancelIoEx calls should not cause the client to give up on the connection.
(await _systemClient.CancelIoPipe(new() { MsDelays = msDelays })).ShouldBeTrue();
var act = async () => cancelIoResult = await _systemClient.CancelIoPipe(new() { MsDelays = msDelays });
await act.ShouldNotThrowAsync();

//Make sure the connection is still working
(await _systemClient.Delay()).ShouldBeTrue();

cancelIoResult.ShouldBeTrue();
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion src/UiPath.CoreIpc.Tests/SystemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public async Task DownloadNoRead()
await Guid();
}
protected abstract TBuilder CreateSystemClientBuilder();
protected TBuilder SystemClientBuilder() => CreateSystemClientBuilder().SerializeParametersAsObjects().RequestTimeout(RequestTimeout).Logger(_serviceProvider);
protected TBuilder SystemClientBuilder() => CreateSystemClientBuilder().RequestTimeout(RequestTimeout).Logger(_serviceProvider);
[Fact]
public async Task BeforeCall()
{
Expand Down
27 changes: 8 additions & 19 deletions src/UiPath.CoreIpc/Client/ServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ class ServiceClient<TInterface> : IServiceClient, IConnectionKey where TInterfac
private Server _server;
private ClientConnection _clientConnection;

internal ServiceClient(ISerializer serializer, TimeSpan requestTimeout, ILogger logger, ConnectionFactory connectionFactory, string sslServer = null, BeforeCallHandler beforeCall = null, bool objectParameters = false, EndpointSettings serviceEndpoint = null)
internal ServiceClient(ISerializer serializer, TimeSpan requestTimeout, ILogger logger, ConnectionFactory connectionFactory, string sslServer = null, BeforeCallHandler beforeCall = null, EndpointSettings serviceEndpoint = null)
{
ObjectParameters = objectParameters;
_serializer = serializer;
_requestTimeout = requestTimeout;
_logger = logger;
Expand All @@ -40,15 +39,14 @@ internal ServiceClient(ISerializer serializer, TimeSpan requestTimeout, ILogger
public virtual string Name => _connection?.Name;
private bool LogEnabled => _logger.Enabled();
Connection IServiceClient.Connection => _connection;
public bool ObjectParameters { get; init; }

public TInterface CreateProxy()
{
var proxy = DispatchProxy.Create<TInterface, IpcProxy>();
(proxy as IpcProxy).ServiceClient = this;
return proxy;
}

public override int GetHashCode() => HashCode;

private void OnNewConnection(Connection connection, bool alreadyHasServer = false)
Expand Down Expand Up @@ -98,25 +96,20 @@ async Task<TResult> Invoke()
await _beforeCall(new(newConnection, method, args), token);
}
var requestId = _connection.NewRequestId();
var request = new Request(typeof(TInterface).Name, requestId, methodName, serializedArguments, ObjectParameters ? args : null, messageTimeout.TotalSeconds)
var request = new Request(typeof(TInterface).Name, requestId, methodName, serializedArguments, messageTimeout.TotalSeconds)
{
UploadStream = uploadStream
};
if (LogEnabled)
{
Log($"IpcClient calling {methodName} {requestId} {Name}.");
}
if (ObjectParameters && !method.ReturnType.IsGenericType)
{
await _connection.Send(request, token);
return default;
}
var response = await _connection.RemoteCall(request, token);
if (LogEnabled)
{
Log($"IpcClient called {methodName} {requestId} {Name}.");
}
return response.Deserialize<TResult>(_serializer, ObjectParameters);
return response.Deserialize<TResult>(_serializer);
}
catch (Exception ex)
{
Expand All @@ -129,10 +122,8 @@ async Task<TResult> Invoke()
}
void SerializeArguments()
{
if (!ObjectParameters)
{
serializedArguments = new string[args.Length];
}
serializedArguments = new string[args.Length];

for (int index = 0; index < args.Length; index++)
{
switch (args[index])
Expand All @@ -150,10 +141,8 @@ void SerializeArguments()
args[index] = "";
break;
}
if (!ObjectParameters)
{
serializedArguments[index] = _serializer.Serialize(args[index]);
}

serializedArguments[index] = _serializer.Serialize(args[index]);
}
}
}
Expand Down
13 changes: 0 additions & 13 deletions src/UiPath.CoreIpc/Client/ServiceClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public abstract class ServiceClientBuilder<TDerived, TInterface> where TInterfac
protected object _callbackInstance;
protected TaskScheduler _taskScheduler;
protected string _sslServer;
protected bool _objectParameters;

protected ServiceClientBuilder(Type callbackContract, IServiceProvider serviceProvider)
{
Expand Down Expand Up @@ -53,18 +52,6 @@ public TDerived Logger(ILogger logger)
_logger = logger;
return (TDerived)this;
}
/// <summary>
/// By default, method parameters are serialized as json strings. Setting this allows serialization as json objects.
/// This should improve performance for large strings, but decrease it for many small objects.
/// Setting it breaks compatibility with older servers.
/// So a proxy with this setting will only be able to connect to servers that understand the new encoding.
/// </summary>
/// <returns>this</returns>
public TDerived SerializeParametersAsObjects()
{
_objectParameters = true;
return (TDerived)this;
}

public TDerived Logger(IServiceProvider serviceProvider) => Logger(serviceProvider.GetRequiredService<ILogger<TInterface>>());

Expand Down
2 changes: 0 additions & 2 deletions src/UiPath.CoreIpc/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ internal async ValueTask<Response> RemoteCall(Request request, CancellationToken
}
internal ValueTask Send(Request request, CancellationToken token)
{
Debug.Assert(request.Parameters == null || request.ObjectParameters == null);
var uploadStream = request.UploadStream;
var requestBytes = SerializeToStream(request);
return uploadStream == null ?
Expand All @@ -100,7 +99,6 @@ Task CancelServerCall(string requestId) =>
}
internal ValueTask Send(Response response, CancellationToken cancellationToken)
{
Debug.Assert(response.Data == null || response.ObjectData == null);
var responseBytes = SerializeToStream(response);
return response.DownloadStream == null ?
SendMessage(MessageType.Response, responseBytes, cancellationToken) :
Expand Down
13 changes: 5 additions & 8 deletions src/UiPath.CoreIpc/Dtos.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,40 @@
namespace UiPath.CoreIpc;
public class Message
{
internal bool ObjectParameters { get; set; }
internal Type CallbackContract { get; set; }
[JsonIgnore]
public IClient Client { get; set; }
[JsonIgnore]
public TimeSpan RequestTimeout { get; set; }
public TCallbackInterface GetCallback<TCallbackInterface>() where TCallbackInterface : class =>
Client.GetCallback<TCallbackInterface>(CallbackContract, ObjectParameters);
Client.GetCallback<TCallbackInterface>(CallbackContract);
public void ImpersonateClient(Action action) => Client.Impersonate(action);
}
public class Message<TPayload> : Message
{
public Message(TPayload payload) => Payload = payload;
public TPayload Payload { get; }
}
record Request(string Endpoint, string Id, string MethodName, string[] Parameters, object[] ObjectParameters, double TimeoutInSeconds)
record Request(string Endpoint, string Id, string MethodName, string[] Parameters, double TimeoutInSeconds)
{
internal Stream UploadStream { get; set; }
public override string ToString() => $"{Endpoint} {MethodName} {Id}.";
internal bool HasObjectParameters => ObjectParameters is not null;
internal TimeSpan GetTimeout(TimeSpan defaultTimeout) => TimeoutInSeconds == 0 ? defaultTimeout : TimeSpan.FromSeconds(TimeoutInSeconds);
}
record CancellationRequest(string RequestId);
record Response(string RequestId, string Data = null, object ObjectData = null, Error Error = null)
record Response(string RequestId, string Data = null, Error Error = null)
{
internal Stream DownloadStream { get; set; }
public static Response Fail(Request request, Exception ex) => new(request.Id, Error: ex.ToError());
public static Response Success(Request request, string data) => new(request.Id, data);
public static Response Success(Request request, Stream downloadStream) => new(request.Id) { DownloadStream = downloadStream };
public TResult Deserialize<TResult>(ISerializer serializer, bool objectParameters)
public TResult Deserialize<TResult>(ISerializer serializer)
{
if (Error != null)
{
throw new RemoteException(Error);
}
return (TResult)(DownloadStream ?? (objectParameters ?
serializer.Deserialize(ObjectData, typeof(TResult)) : serializer.Deserialize(Data ?? "", typeof(TResult))));
return (TResult)(DownloadStream ?? serializer.Deserialize(Data ?? "", typeof(TResult)));
}
}
[Serializable]
Expand Down
13 changes: 2 additions & 11 deletions src/UiPath.CoreIpc/IpcJsonSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@ namespace UiPath.CoreIpc;
public interface ISerializer
{
ValueTask<T> DeserializeAsync<T>(Stream json);
object Deserialize(object json, Type type);
void Serialize(object obj, Stream stream);
string Serialize(object obj);
object Deserialize(string json, Type type);
}
class IpcJsonSerializer : ISerializer, IArrayPool<char>
{
static readonly JsonSerializer ObjectArgsSerializer = new(){ DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, NullValueHandling = NullValueHandling.Ignore,
CheckAdditionalContent = true };
static readonly JsonSerializer StringArgsSerializer = new(){ CheckAdditionalContent = true };
#if !NET461
[AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]
Expand All @@ -27,15 +24,9 @@ public async ValueTask<T> DeserializeAsync<T>(Stream json)
await json.CopyToAsync(stream);
stream.Position = 0;
using var reader = CreateReader(new StreamReader(stream));
return ObjectArgsSerializer.Deserialize<T>(reader);
return StringArgsSerializer.Deserialize<T>(reader);
}
public object Deserialize(object json, Type type) => json switch
{
JToken token => token.ToObject(type, ObjectArgsSerializer),
{ } => type.IsAssignableFrom(json.GetType()) ? json : new JValue(json).ToObject(type),
null => null,
};
public void Serialize(object obj, Stream stream) => Serialize(obj, new StreamWriter(stream), ObjectArgsSerializer);
public void Serialize(object obj, Stream stream) => Serialize(obj, new StreamWriter(stream), StringArgsSerializer);
private void Serialize(object obj, TextWriter streamWriter, JsonSerializer serializer)
{
using var writer = new JsonTextWriter(streamWriter) { ArrayPool = this, CloseOutput = false };
Expand Down
4 changes: 2 additions & 2 deletions src/UiPath.CoreIpc/NamedPipe/NamedPipeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ interface INamedPipeKey : IConnectionKey

class NamedPipeClient<TInterface> : ServiceClient<TInterface>, INamedPipeKey where TInterface : class
{
public NamedPipeClient(string serverName, string pipeName, ISerializer serializer, TimeSpan requestTimeout, bool allowImpersonation, ILogger logger, ConnectionFactory connectionFactory, string sslServer, BeforeCallHandler beforeCall, bool objectParameters, EndpointSettings serviceEndpoint)
: base(serializer, requestTimeout, logger, connectionFactory, sslServer, beforeCall, objectParameters, serviceEndpoint)
public NamedPipeClient(string serverName, string pipeName, ISerializer serializer, TimeSpan requestTimeout, bool allowImpersonation, ILogger logger, ConnectionFactory connectionFactory, string sslServer, BeforeCallHandler beforeCall, EndpointSettings serviceEndpoint)
: base(serializer, requestTimeout, logger, connectionFactory, sslServer, beforeCall, serviceEndpoint)
{
ServerName = serverName;
PipeName = pipeName;
Expand Down
2 changes: 1 addition & 1 deletion src/UiPath.CoreIpc/NamedPipe/NamedPipeClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public TDerived AllowImpersonation()
}

protected override TInterface BuildCore(EndpointSettings serviceEndpoint) =>
new NamedPipeClient<TInterface>(_serverName, _pipeName, _serializer, _requestTimeout, _allowImpersonation, _logger, _connectionFactory, _sslServer, _beforeCall, _objectParameters, serviceEndpoint).CreateProxy();
new NamedPipeClient<TInterface>(_serverName, _pipeName, _serializer, _requestTimeout, _allowImpersonation, _logger, _connectionFactory, _sslServer, _beforeCall, serviceEndpoint).CreateProxy();
}

public class NamedPipeClientBuilder<TInterface> : NamedPipeClientBuilderBase<NamedPipeClientBuilder<TInterface>, TInterface> where TInterface : class
Expand Down
Loading

0 comments on commit 22df014

Please sign in to comment.