From 62faafef5bcf8345083c9dd40815c51ac436d585 Mon Sep 17 00:00:00 2001 From: Elad Zelingher Date: Tue, 26 Aug 2025 14:26:59 -0400 Subject: [PATCH 1/7] Some improvements based on Zap's fork --- .../Newtonsoft/JTokenMessageParser.cs | 22 +++++++-------- .../Owin/OwinWebSocketTransport.cs | 2 +- .../WebSockets/WebSocketWrapperConnection.cs | 8 +++--- .../Client/Callee/InvocationCalleeeTest.cs | 2 +- .../Client/Caller/CallerTest.cs | 2 +- .../Integration/AuthenticationClientTests.cs | 28 +++++++++++++------ .../Integration/CallerDealerTests.cs | 1 + .../Core/Utilities/TaskExtensions.cs | 2 +- .../Callbacks/Async/AsyncOperationCallback.cs | 2 +- .../Callbacks/Sync/SyncCallbackBase.cs | 7 ++++- .../WampCalleeProxyInvocationHandler.cs | 13 +++++++-- .../CalleeProxy/ClientInvocationHandler.cs | 20 ++++++++----- .../ServiceHostedRealmContainer.cs | 4 ++- .../WAMP2/V2/Client/PubSub/WampSubscriber.cs | 2 +- .../WampSharp/WAMP2/V2/Client/WampChannel.cs | 5 ++-- .../WAMP2/V2/Client/WampPendingRequestBase.cs | 2 +- .../V2/Core/Contracts/Error/WampErrors.cs | 5 ++++ .../V2/Realm/Binded/WampGoodbyeExtensions.cs | 2 +- .../V2/Testament/WampTestamentService.cs | 2 +- 19 files changed, 83 insertions(+), 48 deletions(-) diff --git a/src/netstandard/Default/WampSharp.NewtonsoftJson/Newtonsoft/JTokenMessageParser.cs b/src/netstandard/Default/WampSharp.NewtonsoftJson/Newtonsoft/JTokenMessageParser.cs index cad08b4dd..f9a1a91d8 100644 --- a/src/netstandard/Default/WampSharp.NewtonsoftJson/Newtonsoft/JTokenMessageParser.cs +++ b/src/netstandard/Default/WampSharp.NewtonsoftJson/Newtonsoft/JTokenMessageParser.cs @@ -39,13 +39,13 @@ public WampMessage Parse(string text) public string Format(WampMessage message) { - StringWriter writer = new StringWriter(); - JsonTextWriter jsonWriter = new JsonTextWriter(writer); + using StringWriter writer = new StringWriter(); + using JsonTextWriter jsonWriter = new JsonTextWriter(writer); jsonWriter.Formatting = Formatting.None; object[] array = mMessageFormatter.Format(message); mSerializer.Serialize(jsonWriter, array); string result = writer.ToString(); - + mLogger.DebugFormat("Formatted message {JsonMessage}", result); return result; } @@ -59,17 +59,15 @@ public WampMessage Parse(Stream stream) { try { - using (JsonReader reader = new JsonTextReader(new StreamReader(stream)) {CloseInput = false}) - { - JToken parsed = JToken.ReadFrom(reader); - - if (mLogger.IsDebugEnabled()) - { - mLogger.DebugFormat("Trying to parse message {JsonMessage}", parsed.ToString(Formatting.None)); - } + using JsonReader reader = new JsonTextReader(new StreamReader(stream)) {CloseInput = false}; + JToken parsed = JToken.ReadFrom(reader); - return mMessageFormatter.Parse(parsed); + if (mLogger.IsDebugEnabled()) + { + mLogger.DebugFormat("Trying to parse message {JsonMessage}", parsed.ToString(Formatting.None)); } + + return mMessageFormatter.Parse(parsed); } catch (Exception ex) { diff --git a/src/netstandard/Extensions/WampSharp.Owin/Owin/OwinWebSocketTransport.cs b/src/netstandard/Extensions/WampSharp.Owin/Owin/OwinWebSocketTransport.cs index 46f4f177a..9681423ee 100644 --- a/src/netstandard/Extensions/WampSharp.Owin/Owin/OwinWebSocketTransport.cs +++ b/src/netstandard/Extensions/WampSharp.Owin/Owin/OwinWebSocketTransport.cs @@ -27,7 +27,7 @@ public OwinWebSocketTransport base(authenticatorFactory) { mHandler = this.EmptyHandler; - app.Use(HttpHandler); + AppBuilderUseExtensions.Use(app, HttpHandler); mMaxFrameSize = maxFrameSize; } diff --git a/src/netstandard/Extensions/WampSharp.WebSockets/WebSockets/WebSocketWrapperConnection.cs b/src/netstandard/Extensions/WampSharp.WebSockets/WebSockets/WebSocketWrapperConnection.cs index c0ba6b610..4feb21258 100644 --- a/src/netstandard/Extensions/WampSharp.WebSockets/WebSockets/WebSocketWrapperConnection.cs +++ b/src/netstandard/Extensions/WampSharp.WebSockets/WebSockets/WebSocketWrapperConnection.cs @@ -92,13 +92,13 @@ private ArraySegment GetMessageInBytes(WampMessage message) protected abstract WebSocketMessageType WebSocketMessageType { get; } - protected async void InnerConnect() + protected void InnerConnect() { - bool connected = await TryConnect(); + bool connected = Task.Run(TryConnect, mCancellationToken).ConfigureAwait(false).GetAwaiter().GetResult(); if (connected) { - await Task.Run(this.RunAsync, mCancellationToken).ConfigureAwait(false); + Task.Run(RunAsync, mCancellationToken); } } @@ -138,7 +138,7 @@ data is very small. // Buffer for received bits. ArraySegment receivedDataBuffer = new ArraySegment(new byte[maxMessageSize]); - MemoryStream memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new MemoryStream(); // Checks WebSocket state. while (IsConnected && !mCancellationToken.IsCancellationRequested) diff --git a/src/netstandard/Tests/WampSharp.Tests.Wampv2/Client/Callee/InvocationCalleeeTest.cs b/src/netstandard/Tests/WampSharp.Tests.Wampv2/Client/Callee/InvocationCalleeeTest.cs index f8b07aa84..204436759 100644 --- a/src/netstandard/Tests/WampSharp.Tests.Wampv2/Client/Callee/InvocationCalleeeTest.cs +++ b/src/netstandard/Tests/WampSharp.Tests.Wampv2/Client/Callee/InvocationCalleeeTest.cs @@ -129,7 +129,7 @@ public override void Act() IWampChannel channel = playground.GetChannel(mDealer, "realm1", mBinding); - channel.Open(); + channel.Open().Wait(); Task register = channel.RealmProxy.RpcCatalog.Register(mOperation, new RegisterOptions()); diff --git a/src/netstandard/Tests/WampSharp.Tests.Wampv2/Client/Caller/CallerTest.cs b/src/netstandard/Tests/WampSharp.Tests.Wampv2/Client/Caller/CallerTest.cs index 050dce505..a29ed7b72 100644 --- a/src/netstandard/Tests/WampSharp.Tests.Wampv2/Client/Caller/CallerTest.cs +++ b/src/netstandard/Tests/WampSharp.Tests.Wampv2/Client/Caller/CallerTest.cs @@ -95,7 +95,7 @@ public override void Act() IWampChannel channel = playground.GetChannel(mServerMock, "realm1", mBinding); - channel.Open(); + channel.Open().Wait(); mCallAction(channel.RealmProxy.RpcCatalog, mCallbackMock); } diff --git a/src/netstandard/Tests/WampSharp.Tests.Wampv2/Integration/AuthenticationClientTests.cs b/src/netstandard/Tests/WampSharp.Tests.Wampv2/Integration/AuthenticationClientTests.cs index 4301693b7..2b9cb21c7 100644 --- a/src/netstandard/Tests/WampSharp.Tests.Wampv2/Integration/AuthenticationClientTests.cs +++ b/src/netstandard/Tests/WampSharp.Tests.Wampv2/Integration/AuthenticationClientTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; +using System.Threading.Tasks; using Newtonsoft.Json.Linq; using NUnit.Framework; using WampSharp.Binding; @@ -25,7 +26,7 @@ public void AuthenticatorSendsDetailsToHello() new CustomAuthenticator { AuthenticationId = "peter", - AuthenticationMethods = new string[] {"ticket"} + AuthenticationMethods = new string[] { "ticket" } }; HelloMock mock = new HelloMock(); @@ -41,6 +42,7 @@ public void AuthenticatorSendsDetailsToHello() channel.Open(); + mock.HelloCalled.Task.Wait(); IDictionary deserializedDetails = mock.Details.OriginalValue.Deserialize> (); @@ -84,8 +86,9 @@ public void AuthenticatorGetsChallengeMessage() channel.Open(); + authenticator.Authenticated.Task.Wait(); Assert.That(authenticator.AuthMethod, Is.EqualTo("ticket")); - + Assert.That(authenticator.Extra.OriginalValue.Deserialize(), Is.EqualTo(myChallengeDetails)); } @@ -101,13 +104,13 @@ public void AuthenticatorAuthenticateResultCallsAuthenticate() { return new AuthenticationResponse() { - Extra = new MyAuthenticateExtraData() {Secret1 = 3}, + Extra = new MyAuthenticateExtraData() { Secret1 = 3 }, Signature = "secretsignature" }; }) { AuthenticationId = "peter", - AuthenticationMethods = new string[] {"ticket"} + AuthenticationMethods = new string[] { "ticket" } }; AuthenticateMock mock = new AuthenticateMock("ticket"); @@ -123,10 +126,11 @@ public void AuthenticatorAuthenticateResultCallsAuthenticate() channel.Open(); + mock.Authenticated.Task.Wait(); Assert.That(mock.Signature, Is.EqualTo("secretsignature")); - IDictionary deserializedExtra = + IDictionary deserializedExtra = mock.Extra.OriginalValue.Deserialize>(); Assert.That(deserializedExtra["secret1"].Deserialize(), @@ -166,7 +170,7 @@ public void AuthenticatorAuthenticateExceptionCallsAbort() jsonBinding, authenticator); - channel.Open(); + Assert.ThrowsAsync(channel.Open); Assert.That(mock.Reason, Is.EqualTo("some reason")); @@ -194,7 +198,7 @@ public override bool Equals(object obj) if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; - return Equals((MyAbortDetails) obj); + return Equals((MyAbortDetails)obj); } public override int GetHashCode() @@ -221,6 +225,7 @@ public override void Abort(IWampSessionClient client, AbortDetails details, stri private class AuthenticateMock : ChallengeMock { + public TaskCompletionSource Authenticated { get; } = new TaskCompletionSource(); public string Signature { get; private set; } public AuthenticateExtraData Extra { get; private set; } @@ -232,6 +237,7 @@ public override void Authenticate(IWampSessionClient client, string signature, A { Extra = extra; Signature = signature; + Authenticated.SetResult(1); } } @@ -262,12 +268,14 @@ private class HelloMock : HelloMock private class HelloMock : MockServer { + public TaskCompletionSource HelloCalled { get; } = new TaskCompletionSource(); public HelloDetails Details { get; private set; } public override void Hello(IWampSessionClient client, string realm, HelloDetails details) { Details = details; + HelloCalled.SetResult(0); } } @@ -392,8 +400,9 @@ public void Unsubscribe(IWampSubscriber subscriber, long requestId, long subscri private class CustomAuthenticator : IWampClientAuthenticator { private readonly Func mAuthenticate; + public TaskCompletionSource Authenticated { get; } = new TaskCompletionSource(); - public CustomAuthenticator() : + public CustomAuthenticator() : this((authMethod, extra) => new AuthenticationResponse()) { } @@ -407,6 +416,7 @@ public AuthenticationResponse Authenticate(string authmethod, ChallengeDetails e { Extra = extra; AuthMethod = authmethod; + Authenticated.SetResult(0); return mAuthenticate(authmethod, extra); } @@ -435,7 +445,7 @@ public override bool Equals(object obj) if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; - return Equals((MyChallengeDetails) obj); + return Equals((MyChallengeDetails)obj); } public override int GetHashCode() diff --git a/src/netstandard/Tests/WampSharp.Tests.Wampv2/Integration/CallerDealerTests.cs b/src/netstandard/Tests/WampSharp.Tests.Wampv2/Integration/CallerDealerTests.cs index b0eb79504..8597e0003 100644 --- a/src/netstandard/Tests/WampSharp.Tests.Wampv2/Integration/CallerDealerTests.cs +++ b/src/netstandard/Tests/WampSharp.Tests.Wampv2/Integration/CallerDealerTests.cs @@ -102,6 +102,7 @@ public async Task ErrorsService(int number, object[] arguments, string errorUri) } else { + await Task.WhenAny(result); AggregateException exception = result.Exception; Exception actualException = exception.InnerException; Assert.That(actualException, Is.TypeOf()); diff --git a/src/netstandard/WampSharp/Core/Utilities/TaskExtensions.cs b/src/netstandard/WampSharp/Core/Utilities/TaskExtensions.cs index b3b8ae3ca..7fb124544 100644 --- a/src/netstandard/WampSharp/Core/Utilities/TaskExtensions.cs +++ b/src/netstandard/WampSharp/Core/Utilities/TaskExtensions.cs @@ -136,7 +136,7 @@ private static Task ContinueWithSafe(this TTask task, F throw new ArgumentNullException(nameof(transform)); } - TaskCompletionSource taskResult = new TaskCompletionSource(); + TaskCompletionSource taskResult = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); task.ContinueWith(_ => { diff --git a/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/Async/AsyncOperationCallback.cs b/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/Async/AsyncOperationCallback.cs index 959d9550e..6b2563bac 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/Async/AsyncOperationCallback.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/Async/AsyncOperationCallback.cs @@ -10,7 +10,7 @@ namespace WampSharp.V2.CalleeProxy { internal class AsyncOperationCallback : IWampRawRpcOperationClientCallback { - private readonly TaskCompletionSource mTask = new TaskCompletionSource(); + private readonly TaskCompletionSource mTask = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); private readonly IOperationResultExtractor mResultExtractor; public AsyncOperationCallback(IOperationResultExtractor resultExtractor) diff --git a/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/Sync/SyncCallbackBase.cs b/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/Sync/SyncCallbackBase.cs index 779653e65..8b1c3e294 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/Sync/SyncCallbackBase.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/Sync/SyncCallbackBase.cs @@ -8,7 +8,7 @@ namespace WampSharp.V2.CalleeProxy { - internal abstract class SyncCallbackBase : IWampRawRpcOperationClientCallback + internal abstract class SyncCallbackBase : IWampRawRpcOperationClientCallback, IDisposable { private readonly ManualResetEvent mWaitHandle = new ManualResetEvent(false); @@ -58,5 +58,10 @@ public void SetException(Exception exception) Exception = exception; mWaitHandle.Set(); } + + public void Dispose() + { + mWaitHandle?.Dispose(); + } } } diff --git a/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/WampCalleeProxyInvocationHandler.cs b/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/WampCalleeProxyInvocationHandler.cs index b907356af..70d6aba40 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/WampCalleeProxyInvocationHandler.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/Callbacks/WampCalleeProxyInvocationHandler.cs @@ -14,7 +14,7 @@ public T Invoke(ICalleeProxyInterceptor interceptor, MethodInfo method, IOper { Type unwrapped = TaskExtensions.UnwrapReturnType(method.ReturnType); - SyncCallback callback = InnerInvokeSync(interceptor, method, extractor, arguments, unwrapped); + using SyncCallback callback = InnerInvokeSync(interceptor, method, extractor, arguments, unwrapped); WaitForResult(callback); @@ -80,9 +80,16 @@ protected virtual void WaitForResult(SyncCallback callback) callback.Wait(Timeout.Infinite); } - protected virtual Task AwaitForResult(AsyncOperationCallback asyncOperationCallback, CancellationTokenRegistration registration) + protected virtual async Task AwaitForResult(AsyncOperationCallback asyncOperationCallback, CancellationTokenRegistration registration) { - return asyncOperationCallback.Task; + try + { + return await asyncOperationCallback.Task.ConfigureAwait(false); + } + finally + { + registration.Dispose(); + } } } } \ No newline at end of file diff --git a/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/ClientInvocationHandler.cs b/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/ClientInvocationHandler.cs index 0fa38d258..b06746dc5 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/ClientInvocationHandler.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Api/CalleeProxy/ClientInvocationHandler.cs @@ -10,7 +10,7 @@ namespace WampSharp.V2.CalleeProxy { - internal class ClientInvocationHandler : WampCalleeProxyInvocationHandler + internal class ClientInvocationHandler : WampCalleeProxyInvocationHandler, IDisposable { #region Data Members @@ -19,7 +19,7 @@ internal class ClientInvocationHandler : WampCalleeProxyInvocationHandler private TaskCompletionSource mDisconnectionTaskCompletionSource; - private ManualResetEvent mDisconnectionWaitHandle; + private readonly ManualResetEvent mDisconnectionWaitHandle; private Exception mDisconnectionException; private readonly CallOptions mEmptyOptions = new CallOptions(); @@ -34,7 +34,7 @@ public ClientInvocationHandler(IWampRpcOperationCatalogProxy catalogProxy, mCatalogProxy = catalogProxy; mMonitor = monitor; - mDisconnectionTaskCompletionSource = new TaskCompletionSource(); + mDisconnectionTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); mDisconnectionWaitHandle = new ManualResetEvent(false); mMonitor.ConnectionError += OnConnectionError; @@ -56,8 +56,8 @@ private void OnConnectionBroken(object sender, WampSessionCloseEventArgs e) Exception exception = new WampConnectionBrokenException(e); SetException(exception); - mDisconnectionTaskCompletionSource = new TaskCompletionSource(); - mDisconnectionWaitHandle = new ManualResetEvent(false); + mDisconnectionTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + mDisconnectionWaitHandle.Reset(); } private void OnConnectionError(object sender, WampConnectionErrorEventArgs e) @@ -127,7 +127,13 @@ protected override IWampCancellableInvocationProxy Invoke(ICalleeProxyIntercepto procedureUri, arguments); } - -#endregion + public void Dispose() + { + mMonitor.ConnectionError -= OnConnectionError; + mMonitor.ConnectionBroken -= OnConnectionBroken; + mDisconnectionWaitHandle?.Dispose(); + } + + #endregion } } \ No newline at end of file diff --git a/src/netstandard/WampSharp/WAMP2/V2/Api/Server/ServiceRealm/ServiceHostedRealmContainer.cs b/src/netstandard/WampSharp/WAMP2/V2/Api/Server/ServiceRealm/ServiceHostedRealmContainer.cs index b9f54a844..0a3d9c3df 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Api/Server/ServiceRealm/ServiceHostedRealmContainer.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Api/Server/ServiceRealm/ServiceHostedRealmContainer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Reactive.Concurrency; +using System.Threading.Tasks; using WampSharp.V2.Realm; namespace WampSharp.V2 @@ -32,7 +33,8 @@ private IWampHostedRealm BuildRealm(string name) channel.RealmProxy.Monitor.ConnectionEstablished += connectionEstablished; - channel.Open(); + // TODO: Check this + Task.Run(channel.Open).ConfigureAwait(false).GetAwaiter().GetResult(); channel.RealmProxy.Monitor.ConnectionEstablished -= connectionEstablished; diff --git a/src/netstandard/WampSharp/WAMP2/V2/Client/PubSub/WampSubscriber.cs b/src/netstandard/WampSharp/WAMP2/V2/Client/PubSub/WampSubscriber.cs index d6783ba2e..bcd02383a 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Client/PubSub/WampSubscriber.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Client/PubSub/WampSubscriber.cs @@ -61,7 +61,7 @@ public Task Subscribe(IWampRawTopicClientSubscriber subscriber private Task Unsubscribe(Subscription subscription) { - TaskCompletionSource completionSource = new TaskCompletionSource(); + TaskCompletionSource completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); Task result = completionSource.Task; lock (mLock) diff --git a/src/netstandard/WampSharp/WAMP2/V2/Client/WampChannel.cs b/src/netstandard/WampSharp/WAMP2/V2/Client/WampChannel.cs index bc08f142e..29c79cb22 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Client/WampChannel.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Client/WampChannel.cs @@ -44,7 +44,7 @@ private void OnConnectionError(object sender, WampConnectionErrorEventArgs e) public IWampRealmProxy RealmProxy => mClient.Realm; - public Task Open() + public async Task Open() { if (Interlocked.CompareExchange(ref mConnectCalled, 1, 0) != 0) { @@ -54,8 +54,9 @@ public Task Open() else { Task openTask = mClient.OpenTask; + await Task.Yield(); // avoid the blocking mConnection.Connect() to block us from returning the task early. mConnection.Connect(); - return openTask; + await openTask.ConfigureAwait(false); } } diff --git a/src/netstandard/WampSharp/WAMP2/V2/Client/WampPendingRequestBase.cs b/src/netstandard/WampSharp/WAMP2/V2/Client/WampPendingRequestBase.cs index 65bb7f737..e1471c6b0 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Client/WampPendingRequestBase.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Client/WampPendingRequestBase.cs @@ -7,7 +7,7 @@ namespace WampSharp.V2.Client { internal class WampPendingRequestBase : IWampPendingRequest { - private readonly TaskCompletionSource mTaskCompletionSource = new TaskCompletionSource(); + private readonly TaskCompletionSource mTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); private readonly IWampFormatter mFormatter; public WampPendingRequestBase(IWampFormatter formatter) diff --git a/src/netstandard/WampSharp/WAMP2/V2/Core/Contracts/Error/WampErrors.cs b/src/netstandard/WampSharp/WAMP2/V2/Core/Contracts/Error/WampErrors.cs index c435766bc..5960e4b0a 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Core/Contracts/Error/WampErrors.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Core/Contracts/Error/WampErrors.cs @@ -62,6 +62,11 @@ public static class WampErrors /// public const string GoodbyeAndOut = "wamp.error.goodbye_and_out"; + /// + /// A *Peer* received invalid WAMP protocol message (e.g. HELLO message after session was already established) - used as a `ABORT` reply reason. + /// + public const string ProtocolViolation = "wamp.error.protocol_violation"; + /// /// A *Dealer* could not perform a call, since the procedure called does not exist. /// diff --git a/src/netstandard/WampSharp/WAMP2/V2/Realm/Binded/WampGoodbyeExtensions.cs b/src/netstandard/WampSharp/WAMP2/V2/Realm/Binded/WampGoodbyeExtensions.cs index 06aba3960..cf3a3ba63 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Realm/Binded/WampGoodbyeExtensions.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Realm/Binded/WampGoodbyeExtensions.cs @@ -11,7 +11,7 @@ public static void SendGoodbye(this IWampClientProxy clientP { clientProxy.GoodbyeSent = true; clientProxy.Goodbye(details, reason); - clientProxy.Realm.Goodbye(clientProxy.Session, details, reason); + clientProxy.Realm?.Goodbye(clientProxy.Session, details, reason); } } } diff --git a/src/netstandard/WampSharp/WAMP2/V2/Testament/WampTestamentService.cs b/src/netstandard/WampSharp/WAMP2/V2/Testament/WampTestamentService.cs index 0a0a2e50f..3f65c7146 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Testament/WampTestamentService.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Testament/WampTestamentService.cs @@ -35,7 +35,7 @@ private void OnSessionClosed(object sender, WampSessionCloseEventArgs e) { if (mSessionIdToTestaments.TryGetValue(sessionId, out testaments)) { - mSessionIdToTestaments.Remove(sessionId); + mSessionIdToTestaments = mSessionIdToTestaments.Remove(sessionId); } } From c5035d8e563692a6d38ba4393c4928b465d90da7 Mon Sep 17 00:00:00 2001 From: Elad Zelingher Date: Tue, 26 Aug 2025 14:43:12 -0400 Subject: [PATCH 2/7] Zap changes to WampSessionClient --- .../V2/Client/Session/WampSessionClient.cs | 417 +++++++++++++----- 1 file changed, 311 insertions(+), 106 deletions(-) diff --git a/src/netstandard/WampSharp/WAMP2/V2/Client/Session/WampSessionClient.cs b/src/netstandard/WampSharp/WAMP2/V2/Client/Session/WampSessionClient.cs index 7ae7c070f..6e38273a4 100644 --- a/src/netstandard/WampSharp/WAMP2/V2/Client/Session/WampSessionClient.cs +++ b/src/netstandard/WampSharp/WAMP2/V2/Client/Session/WampSessionClient.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using WampSharp.Core.Listener; @@ -15,16 +16,15 @@ public class WampSessionClient : IWampSessionClientExtended, private static readonly GoodbyeDetails EmptyGoodbyeDetails = new GoodbyeDetails(); private static readonly AuthenticateExtraData EmptyAuthenticateDetails = new AuthenticateExtraData(); private readonly IWampServerProxy mServerProxy; - private TaskCompletionSource mOpenTask = new TaskCompletionSource(); - private TaskCompletionSource mCloseTask; + private TaskCompletionSource mOpenTask = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private TaskCompletionSource mCloseTask = new TaskCompletionSource(); private GoodbyeMessage mGoodbyeMessage; - private readonly IWampFormatter mFormatter; - private bool mGoodbyeSent; - private readonly IWampClientAuthenticator mAuthenticator; + private readonly IWampClientAuthenticator mAuthenticator; private HelloDetails mSentDetails; - private int mIsConnected = 0; + private SessionState mSessionState = SessionState.Closed; private WampSessionCloseEventArgs mCloseEventArgs; + private readonly object mLock = new object(); private static HelloDetails GetDetails() { @@ -64,12 +64,58 @@ private static HelloDetails GetDetails() public WampSessionClient(IWampRealmProxy realm, IWampFormatter formatter, IWampClientAuthenticator authenticator) { Realm = realm; - mFormatter = formatter; mServerProxy = realm.Proxy; mAuthenticator = authenticator ?? new DefaultWampClientAuthenticator(); } + public long Session { get; private set; } + public event EventHandler ConnectionEstablished; + public event EventHandler ConnectionBroken; + public event EventHandler ConnectionError; + public bool IsConnected => mSessionState == SessionState.Established; + public IWampRealmProxy Realm { get; } + public Task OpenTask => mOpenTask.Task; + + public void OnConnectionOpen() + { + var (_, action) = ChangeState(StateChangeReason.ConnectionOpen); + + if (action == StateChangeAction.SendHello) + { + HelloDetails helloDetails = GetDetails(); + + if (mAuthenticator.AuthenticationId != null) + { + helloDetails.AuthenticationId = mAuthenticator.AuthenticationId; + } + + if (mAuthenticator.AuthenticationMethods != null) + { + helloDetails.AuthenticationMethods = mAuthenticator.AuthenticationMethods; + } + + mServerProxy.Hello(Realm.Name, + helloDetails); + + mSentDetails = helloDetails; + } + } + public void Challenge(string authMethod, ChallengeDetails extra) + { + var (_, action) = ChangeState(StateChangeReason.ReceivedChallenge); + switch (action) + { + case StateChangeAction.SendAuthenticate: + AuthenticateWithServer(authMethod, extra); + break; + case StateChangeAction.SendProtocolViolation: + SendProtocolViolation("The server issued a CHALLENGE from an invalid state"); + break; + } + } + + private void AuthenticateWithServer(string authMethod, ChallengeDetails extra) { try { @@ -79,160 +125,150 @@ public void Challenge(string authMethod, ChallengeDetails extra) string authenticationSignature = response.Signature; + ChangeState(StateChangeReason.SentAuthenticate); // put this right before proxy call, as in mServerProxy.Authenticate it can send WELCOME in synchronous manner mServerProxy.Authenticate(authenticationSignature, authenticationExtraData); } catch (WampAuthenticationException ex) { - mServerProxy.Abort(ex.Details, ex.Reason); - OnConnectionError(ex); + var (_, action) = ChangeState(StateChangeReason.AuthenticationFailed); + if (action == StateChangeAction.SendAbort) + { + mServerProxy.Abort(ex.Details, ex.Reason); + OnConnectionError(ex); + } } } public void Welcome(long session, WelcomeDetails details) { - Session = session; - - Interlocked.CompareExchange(ref mIsConnected, 1, 0); - - mOpenTask.TrySetResult(true); - - OnConnectionEstablished(new WampSessionCreatedEventArgs - (session, mSentDetails, details, new SessionTerminator(this))); + var (_, action) = ChangeState(StateChangeReason.ReceivedWelcome); + switch (action) + { + case StateChangeAction.RaiseConnectionEstablished: + Session = session; + + OnConnectionEstablished(new WampSessionCreatedEventArgs + (session, mSentDetails, details, new SessionTerminator(this))); + mOpenTask.TrySetResult(true); + break; + case StateChangeAction.SendProtocolViolation: + SendProtocolViolation("Received a WELCOME message from an invalid state"); + break; + } } public void Abort(AbortDetails details, string reason) { using (IDisposable proxy = mServerProxy as IDisposable) { - TrySetCloseEventArgs(SessionCloseType.Abort, details, reason); + var (_, action) = ChangeState(StateChangeReason.ReceivedAbort); + if (action == StateChangeAction.Close) + TrySetCloseEventArgs(SessionCloseType.Abort, details, reason); } } public void Goodbye(GoodbyeDetails details, string reason) { + var (_, action) = ChangeState(StateChangeReason.ReceivedGoodbye); using (IDisposable proxy = mServerProxy as IDisposable) { TrySetCloseEventArgs(SessionCloseType.Goodbye, details, reason); - if (!mGoodbyeSent) - { - mGoodbyeSent = true; - mServerProxy.Goodbye(new GoodbyeDetails(), WampErrors.GoodbyeAndOut); - } - else + switch (action) { - mGoodbyeMessage = new GoodbyeMessage(){Details = details, Reason = reason}; + case StateChangeAction.AcceptGoodbye: + Interlocked.CompareExchange(ref mGoodbyeMessage, new GoodbyeMessage { Details = details, Reason = reason }, null); + break; + case StateChangeAction.SendGoodbye: + Interlocked.CompareExchange(ref mGoodbyeMessage, new GoodbyeMessage { Details = details, Reason = reason }, null); + mServerProxy.Goodbye(new GoodbyeDetails(), WampErrors.GoodbyeAndOut); + break; + case StateChangeAction.SendProtocolViolation: + SendProtocolViolation("Received GOODBYE message before session was established"); + break; } + } } - private void RaiseConnectionBroken() + public Task Close(string reason, GoodbyeDetails details) { - TrySetCloseEventArgs(SessionCloseType.Disconnection); - - WampSessionCloseEventArgs closeEventArgs = mCloseEventArgs; - - Interlocked.CompareExchange(ref mIsConnected, 0, 1); - - GoodbyeMessage goodbyeMessage = mGoodbyeMessage; - - if (goodbyeMessage != null) + var (stateData, action) = ChangeState(StateChangeReason.CloseInitiated); + switch (action) { - mCloseTask?.TrySetResult(goodbyeMessage); + case StateChangeAction.SendGoodbye: + mServerProxy.Goodbye(details ?? EmptyGoodbyeDetails, reason ?? WampErrors.CloseNormal); + break; + case StateChangeAction.SendAbort: + mServerProxy.Abort(new AbortDetails { Message = "The client had to close before a session could be established" }, WampErrors.SystemShutdown); + break; + case StateChangeAction.RaiseConnectionNotEstablished: + return Task.FromException(new WampSessionNotEstablishedException("Cannot close an unopened session")); } - SetTasksErrorsIfNeeded(new WampConnectionBrokenException(mCloseEventArgs)); - - mOpenTask = new TaskCompletionSource(); - mCloseTask = null; - mCloseEventArgs = null; - mGoodbyeMessage = null; - - OnConnectionBroken(closeEventArgs); + return stateData.CloseTask.Task; } - private void TrySetCloseEventArgs(SessionCloseType sessionCloseType, - GoodbyeAbortDetails details = null, - string reason = null) + public void OnConnectionClosed() { - if (mCloseEventArgs == null) - { - mCloseEventArgs = new WampSessionCloseEventArgs - (sessionCloseType, Session, - details, - reason); - } + RaiseConnectionBroken(); } - public long Session { get; private set; } - - public IWampRealmProxy Realm { get; } - - public Task OpenTask => mOpenTask.Task; - - public Task Close(string reason, GoodbyeDetails details) + private void RaiseConnectionBroken() { - reason = reason ?? WampErrors.CloseNormal; - details = details ?? EmptyGoodbyeDetails; - - TaskCompletionSource closeTask = - new TaskCompletionSource(); - - mCloseTask = closeTask; + var (stateData, action) = ChangeState(StateChangeReason.ConnectionBroken); + if (action == StateChangeAction.RaiseConnectionBroken) + { + TrySetCloseEventArgs(SessionCloseType.Disconnection); - mGoodbyeSent = true; - mServerProxy.Goodbye(details, reason); + // reset first so that when setting result or exception it won't still hold on old tasks if other thread try to Open immediately after Close task finishes. + Interlocked.Exchange(ref mSentDetails, null); + var closeEventArgs = Interlocked.Exchange(ref mCloseEventArgs, null); + var oldOpenTask = Interlocked.Exchange(ref mOpenTask, new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously)); - return closeTask.Task; - } - - public void OnConnectionOpen() - { - HelloDetails helloDetails = GetDetails(); + if (Interlocked.Exchange(ref mGoodbyeMessage, null) is GoodbyeMessage goodbyeMessage) + { + stateData.CloseTask?.TrySetResult(goodbyeMessage); + } - if (mAuthenticator.AuthenticationId != null) - { - helloDetails.AuthenticationId = mAuthenticator.AuthenticationId; - } + var exception = new WampConnectionBrokenException(closeEventArgs); + oldOpenTask?.TrySetException(exception); + stateData.CloseTask?.TrySetException(exception); - if (mAuthenticator.AuthenticationMethods != null) - { - helloDetails.AuthenticationMethods = mAuthenticator.AuthenticationMethods; + OnConnectionBroken(closeEventArgs); } - - mServerProxy.Hello - (Realm.Name, - helloDetails); - - mSentDetails = helloDetails; } - public void OnConnectionClosed() + private void TrySetCloseEventArgs(SessionCloseType sessionCloseType, + GoodbyeAbortDetails details = null, + string reason = null) { - RaiseConnectionBroken(); + Interlocked.CompareExchange( + ref mCloseEventArgs, + new WampSessionCloseEventArgs(sessionCloseType, Session, details, reason), + null); } public void OnConnectionError(Exception exception) { - SetTasksErrorsIfNeeded(exception); - - OnConnectionError(new WampConnectionErrorEventArgs(exception)); + var (stateData, action) = ChangeState(StateChangeReason.ConnectionError); + if (action == StateChangeAction.RaiseConnectionError) + { + mOpenTask?.TrySetException(exception); + stateData.CloseTask.TrySetException(exception); + OnConnectionError(new WampConnectionErrorEventArgs(exception)); + } } - private void SetTasksErrorsIfNeeded(Exception exception) + private void SendProtocolViolation(string message) { - mOpenTask?.TrySetException(exception); - mCloseTask?.TrySetException(exception); + using (mServerProxy as IDisposable) + { + TrySetCloseEventArgs(SessionCloseType.Abort, new AbortDetails { Message = $"Aborted session with server: '{message}'" }, WampErrors.ProtocolViolation); + mServerProxy.Abort(new AbortDetails { Message = message }, WampErrors.ProtocolViolation); + } } - public event EventHandler ConnectionEstablished; - - public event EventHandler ConnectionBroken; - - public event EventHandler ConnectionError; - - public bool IsConnected => mIsConnected == 1; - protected virtual void OnConnectionEstablished(WampSessionCreatedEventArgs e) { ConnectionEstablished?.Invoke(this, e); @@ -248,6 +284,122 @@ protected virtual void OnConnectionError(WampConnectionErrorEventArgs e) ConnectionError?.Invoke(this, e); } + private (SessionStateData, StateChangeAction) ChangeState(StateChangeReason reason) + { + lock (mLock) + { + var (newSessionState, stateChangeAction) = Transition(mSessionState, reason); + + mSessionState = newSessionState; + + if (reason == StateChangeReason.ConnectionOpen && stateChangeAction != StateChangeAction.Ignore) + { + mCloseTask = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } + + return (new SessionStateData(mSessionState, mCloseTask), stateChangeAction); + } + } + + /* https://wamp-proto.org/wamp_latest_ietf.html#section-4-3 + +--------------+ + +--------(6)-------------> | + | | CLOSED <--------------------------+ + | +------(4)-------------> <---+ | + | | +--------------+ | | + | | | | | + | | (1) (7) | + | | | | | + | | +--------v-----+ | (11) + | | | +---+ | + | | +------------+ ESTABLISHING +----------------+ | + | | | | | | | + | | | +--------------+ | | + | | | | (10) | + | | | (9) | | + | | | | | | + | | (2) +--------v-----+ +--------v-------+ | + | | | | | | | | + | | | +------> FAILED <--(13)-+ CHALLENGING / +-+ + | | | | | | | AUTHENTICATING | + | | | | +--------------+ +----------------+ + | | | (8) | + | | | | | + | | | | | + | | +-------v-------+ | + | | | <-------------------(12)-------------+ + | | | ESTABLISHED | + | | | +--------------+ + | | +---------------+ | + | | | | + | | (3) (5) + | | | | + | | +-------v-------+ +--------v-----+ + | | | +--+ | | + | +-+ SHUTTING DOWN | | | CLOSING | + | | |(14) | | + | +-------^-------+ | +--------------+ + | |----------+ | + +----------------------------------+ + */ + private static (SessionState, StateChangeAction) Transition(SessionState previousState, StateChangeReason reason) + { + return previousState switch + { + _ when reason is StateChangeReason.ConnectionBroken => (SessionState.Closed, StateChangeAction.RaiseConnectionBroken), + _ when reason is StateChangeReason.ConnectionError => (SessionState.Closed, StateChangeAction.RaiseConnectionError), + SessionState.Closed => reason switch + { + StateChangeReason.ConnectionOpen => (SessionState.Establishing, StateChangeAction.SendHello), // 1 + StateChangeReason.CloseInitiated => (SessionState.Closed, StateChangeAction.RaiseConnectionNotEstablished), + _ => (SessionState.Closed, StateChangeAction.Ignore) + }, + SessionState.Establishing => reason switch + { + StateChangeReason.CloseInitiated => (SessionState.Closed, StateChangeAction.SendAbort), // 7 + StateChangeReason.ReceivedWelcome => (SessionState.Established, StateChangeAction.RaiseConnectionEstablished), // 2 + StateChangeReason.ReceivedChallenge => (SessionState.SendingAuthenticate, StateChangeAction.SendAuthenticate), // 10 + StateChangeReason.ReceivedAbort => (SessionState.Closed, StateChangeAction.Close), // 7 + _ => (SessionState.Failed, StateChangeAction.SendProtocolViolation) // 9 + }, + SessionState.SendingAuthenticate => reason switch + { + StateChangeReason.CloseInitiated => (SessionState.Closed, StateChangeAction.SendAbort), + StateChangeReason.SentAuthenticate => (SessionState.Authenticating, StateChangeAction.Ignore), + StateChangeReason.AuthenticationFailed => (SessionState.Closed, StateChangeAction.SendAbort), // 11 + StateChangeReason.ReceivedAbort => (SessionState.Closed, StateChangeAction.Close), + _ => (SessionState.Failed, StateChangeAction.SendProtocolViolation) + }, + SessionState.Authenticating => reason switch + { + StateChangeReason.CloseInitiated => (SessionState.Closed, StateChangeAction.SendAbort), + StateChangeReason.ReceivedWelcome => (SessionState.Established, StateChangeAction.RaiseConnectionEstablished), // 12 + StateChangeReason.AuthenticationFailed => (SessionState.Closed, StateChangeAction.SendAbort), // 11 + StateChangeReason.ReceivedAbort => (SessionState.Closed, StateChangeAction.Close), // 11 + _ => (SessionState.Failed, StateChangeAction.SendProtocolViolation) // 13 + }, + SessionState.Established => reason switch + { + StateChangeReason.CloseInitiated => (SessionState.ShuttingDown, StateChangeAction.SendGoodbye), // 3 + StateChangeReason.ReceivedGoodbye => (SessionState.Closing, StateChangeAction.SendGoodbye), // 5 + StateChangeReason.ReceivedAbort => (SessionState.Closed, StateChangeAction.Close), + _ => (SessionState.Failed, StateChangeAction.SendProtocolViolation) // 8 + }, + SessionState.ShuttingDown => reason switch + { + StateChangeReason.ReceivedGoodbye => (SessionState.ShuttingDown, StateChangeAction.AcceptGoodbye), // 4 + _ => (SessionState.ShuttingDown, StateChangeAction.Ignore), // 14 + }, + SessionState.Closing => reason switch + { + StateChangeReason.ReceivedAbort => (SessionState.Closed, StateChangeAction.Close), + _ => (SessionState.Closing, StateChangeAction.Ignore) + }, + SessionState.Failed => (SessionState.Failed, StateChangeAction.Ignore), + _ => (previousState, StateChangeAction.Ignore) + }; + } + private class SessionTerminator : IWampSessionTerminator { private readonly WampSessionClient mParent; @@ -262,5 +414,58 @@ public void Disconnect(GoodbyeDetails details, string reason) mParent.Close(reason, details); } } + + private class SessionStateData + { + public readonly SessionState State; + public readonly TaskCompletionSource CloseTask; + public SessionStateData(SessionState state, TaskCompletionSource closeTask) + { + State = state; + CloseTask = closeTask; + } + } + + private enum SessionState + { + Closed, + Establishing, + SendingAuthenticate, + Authenticating, + Established, + ShuttingDown, + Closing, + Failed, + } + + private enum StateChangeReason + { + ConnectionOpen, + SentAuthenticate, + ReceivedWelcome, + CloseInitiated, + ReceivedGoodbye, + ConnectionBroken, + ReceivedChallenge, + AuthenticationFailed, + ReceivedAbort, + ConnectionError + } + + private enum StateChangeAction + { + SendHello, + RaiseConnectionEstablished, + SendAuthenticate, + SendGoodbye, + AcceptGoodbye, + RaiseConnectionBroken, + SendProtocolViolation, + SendAbort, + Ignore, + Close, + RaiseConnectionNotEstablished, + RaiseConnectionError + } } -} +} \ No newline at end of file From f8853ed43ba9ed21249dbe879c5bc8c8b40bbf9f Mon Sep 17 00:00:00 2001 From: Elad Zelingher Date: Tue, 26 Aug 2025 14:56:12 -0400 Subject: [PATCH 3/7] Updating CLIFx and AspNetCore and target framework --- .../RunRouterCommand.cs | 4 ++-- .../WampSharp.Samples.AspNetCore.Router.csproj | 8 ++++---- .../Samples/WAMP2/WampSharp.Samples.Callee/Program.cs | 2 ++ .../WAMP2/WampSharp.Samples.Common/SampleCommand.cs | 4 ++-- .../WampSharp.Samples.Common.csproj | 2 +- .../Reflection/SubscriberCommand.cs | 2 ++ 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.AspNetCore.Router/RunRouterCommand.cs b/src/netstandard/Samples/WAMP2/WampSharp.Samples.AspNetCore.Router/RunRouterCommand.cs index bc01b8688..0391c51d6 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.AspNetCore.Router/RunRouterCommand.cs +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.AspNetCore.Router/RunRouterCommand.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using CliFx; using CliFx.Attributes; -using CliFx.Services; +using CliFx.Infrastructure; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -21,7 +21,7 @@ public class RunRouterCommand : ICommand [CommandOption("port", 'p', Description = "The port to listen on for requests. Default value is 8080.", IsRequired = false)] public int Port { get; set; } = 8080; - public async Task ExecuteAsync(IConsole console) + public async ValueTask ExecuteAsync(IConsole console) { WampHost wampHost = new WampHost(); diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.AspNetCore.Router/WampSharp.Samples.AspNetCore.Router.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.AspNetCore.Router/WampSharp.Samples.AspNetCore.Router.csproj index 0a9807abc..f2357e12b 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.AspNetCore.Router/WampSharp.Samples.AspNetCore.Router.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.AspNetCore.Router/WampSharp.Samples.AspNetCore.Router.csproj @@ -2,13 +2,13 @@ Exe - netcoreapp3.1 + net9.0 - - - + + + diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Callee/Program.cs b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Callee/Program.cs index aad78303b..f12a88af9 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Callee/Program.cs +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Callee/Program.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using CliFx; +using CliFx.Attributes; using WampSharp.V2; using WampSharp.Samples.Common; @@ -17,6 +18,7 @@ static async Task Main(string[] args) } } + [Command] public class CalleeCommand : SampleCommand where TService : new() { diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/SampleCommand.cs b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/SampleCommand.cs index 8d4900419..e92d38605 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/SampleCommand.cs +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/SampleCommand.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using CliFx; using CliFx.Attributes; -using CliFx.Services; +using CliFx.Infrastructure; using WampSharp.V2; using WampSharp.V2.Client; using WampSharp.V2.Fluent; @@ -46,7 +46,7 @@ public string Address set => mAddress = value; } - public async Task ExecuteAsync(IConsole console) + public async ValueTask ExecuteAsync(IConsole console) { IWampChannel channel = CreateChannel(); diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/WampSharp.Samples.Common.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/WampSharp.Samples.Common.csproj index aa111c168..b3525d7b1 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/WampSharp.Samples.Common.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/WampSharp.Samples.Common.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Subscriber/Reflection/SubscriberCommand.cs b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Subscriber/Reflection/SubscriberCommand.cs index 2419fc292..e057d969b 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Subscriber/Reflection/SubscriberCommand.cs +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Subscriber/Reflection/SubscriberCommand.cs @@ -1,10 +1,12 @@ using System; using System.Threading.Tasks; +using CliFx.Attributes; using WampSharp.Samples.Common; using WampSharp.V2; namespace WampSharp.Samples.Subscriber.Reflection { + [Command] public class SubscriberCommand : SampleCommand where TService : new() { From 07102880625061b8a13576e60abd40bc21829530 Mon Sep 17 00:00:00 2001 From: Elad Zelingher Date: Tue, 26 Aug 2025 14:59:59 -0400 Subject: [PATCH 4/7] Updating target frameworks --- .../WampSharp.Samples.Callee/WampSharp.Samples.Callee.csproj | 2 +- .../WampSharp.Samples.Caller/WampSharp.Samples.Caller.csproj | 2 +- .../WampSharp.Samples.Common/WampSharp.Samples.Common.csproj | 2 +- .../WampSharp.Samples.Publisher.csproj | 2 +- .../WampSharp.Samples.Subscriber.csproj | 2 +- .../WampSharp.Samples.WampCra.Client.csproj | 2 +- .../WampSharp.Samples.WampCra.Router.csproj | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Callee/WampSharp.Samples.Callee.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Callee/WampSharp.Samples.Callee.csproj index 4b4418a7f..3dee82398 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Callee/WampSharp.Samples.Callee.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Callee/WampSharp.Samples.Callee.csproj @@ -2,7 +2,7 @@ Exe - net461;netcoreapp3.1 + net461;net9.0 8 diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Caller/WampSharp.Samples.Caller.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Caller/WampSharp.Samples.Caller.csproj index fe8a6aa63..d3bd44a98 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Caller/WampSharp.Samples.Caller.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Caller/WampSharp.Samples.Caller.csproj @@ -2,7 +2,7 @@ Exe - net461;netcoreapp3.1 + net461;net9.0 diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/WampSharp.Samples.Common.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/WampSharp.Samples.Common.csproj index b3525d7b1..85fd9e682 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/WampSharp.Samples.Common.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Common/WampSharp.Samples.Common.csproj @@ -1,7 +1,7 @@  - net461;netcoreapp3.1 + net461;net9.0 8 diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Publisher/WampSharp.Samples.Publisher.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Publisher/WampSharp.Samples.Publisher.csproj index fe8a6aa63..d3bd44a98 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Publisher/WampSharp.Samples.Publisher.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Publisher/WampSharp.Samples.Publisher.csproj @@ -2,7 +2,7 @@ Exe - net461;netcoreapp3.1 + net461;net9.0 diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Subscriber/WampSharp.Samples.Subscriber.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Subscriber/WampSharp.Samples.Subscriber.csproj index 6b6530889..fbd1ad13c 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.Subscriber/WampSharp.Samples.Subscriber.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.Subscriber/WampSharp.Samples.Subscriber.csproj @@ -2,7 +2,7 @@ Exe - net461;netcoreapp3.1 + net461;net9.0 8 diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.WampCra.Client/WampSharp.Samples.WampCra.Client.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.WampCra.Client/WampSharp.Samples.WampCra.Client.csproj index c11b25439..d72472f00 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.WampCra.Client/WampSharp.Samples.WampCra.Client.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.WampCra.Client/WampSharp.Samples.WampCra.Client.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp3.1 + net461;net9.0 Exe diff --git a/src/netstandard/Samples/WAMP2/WampSharp.Samples.WampCra.Router/WampSharp.Samples.WampCra.Router.csproj b/src/netstandard/Samples/WAMP2/WampSharp.Samples.WampCra.Router/WampSharp.Samples.WampCra.Router.csproj index 802f1db3b..806618cc3 100644 --- a/src/netstandard/Samples/WAMP2/WampSharp.Samples.WampCra.Router/WampSharp.Samples.WampCra.Router.csproj +++ b/src/netstandard/Samples/WAMP2/WampSharp.Samples.WampCra.Router/WampSharp.Samples.WampCra.Router.csproj @@ -1,6 +1,6 @@  - net461;netcoreapp3.1 + net461;net9.0 Exe From 5b41abb679d02fb93be5aa2c76ee56a81ef0313c Mon Sep 17 00:00:00 2001 From: Elad Zelingher Date: Tue, 26 Aug 2025 15:03:53 -0400 Subject: [PATCH 5/7] Updating workflow file --- .github/workflows/dotnetcore.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 04edd167d..2a370fbff 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -5,17 +5,17 @@ on: [push, pull_request] jobs: build: - runs-on: windows-2019 + runs-on: windows-2025 steps: - uses: actions/checkout@v1 - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: '3.1.x' + dotnet-version: '9.0.x' - name: Build with dotnet working-directory: src/netstandard/ run: dotnet build --configuration Release - name: Test with dotnet working-directory: src/netstandard/ - run: dotnet test --configuration Release + run: dotnet test --configuration Release \ No newline at end of file From 93fa9349bd9fe4278163cbb3ead5a93d5a9650c1 Mon Sep 17 00:00:00 2001 From: Elad Zelingher Date: Tue, 26 Aug 2025 15:19:46 -0400 Subject: [PATCH 6/7] Correction --- .github/workflows/dotnetcore.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 2a370fbff..33599c9ae 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -12,7 +12,9 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: '9.0.x' + dotnet-version: | + '9.0.x' + '3.1.x' - name: Build with dotnet working-directory: src/netstandard/ run: dotnet build --configuration Release From 6741b8dde613ddf77d8a1c266a8f045c12f2903d Mon Sep 17 00:00:00 2001 From: Elad Zelingher Date: Tue, 26 Aug 2025 15:24:43 -0400 Subject: [PATCH 7/7] Correction --- .github/workflows/dotnetcore.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 33599c9ae..508ba7b07 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -13,8 +13,8 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: | - '9.0.x' - '3.1.x' + 9.0.x + 3.1.x - name: Build with dotnet working-directory: src/netstandard/ run: dotnet build --configuration Release