From 7af83b2b493c88992c2708eb615de4c69c2ccec0 Mon Sep 17 00:00:00 2001 From: Collin Dauphinee Date: Tue, 29 Oct 2019 22:19:24 +0000 Subject: [PATCH 1/3] Allowing consumers to inject their own JSON serializer --- AuthHost/AuthHost.csproj | 9 +++ ExampleApplication/ExampleApplication.csproj | 9 +++ .../PusherClient.Tests.Utilities.csproj | 9 +++ .../AcceptanceTests/EventEmitter.cs | 30 +++++----- PusherClient.Tests/PusherClient.Tests.csproj | 9 +++ PusherClient/Channel.cs | 2 + PusherClient/Connection.cs | 57 +++++++++---------- PusherClient/ErrorConstants.cs | 1 + PusherClient/EventEmitter.cs | 17 +++++- PusherClient/GenericPresenceChannel.cs | 5 +- PusherClient/IJsonSerializer.cs | 16 ++++++ PusherClient/IPusher.cs | 6 +- PusherClient/IRequiresJsonSerializer.cs | 7 +++ PusherClient/ITriggerChannels.cs | 2 +- PusherClient/NewtonsoftJsonSerializer.cs | 30 ++++++++++ PusherClient/PresenceChannel.cs | 1 - PusherClient/Pusher.cs | 31 +++++++--- PusherClient/PusherClient.csproj | 12 +++- PusherClient/PusherOptions.cs | 5 ++ pusher-dotnet-client.sln | 15 ++++- 20 files changed, 205 insertions(+), 68 deletions(-) create mode 100644 PusherClient/IJsonSerializer.cs create mode 100644 PusherClient/IRequiresJsonSerializer.cs create mode 100644 PusherClient/NewtonsoftJsonSerializer.cs diff --git a/AuthHost/AuthHost.csproj b/AuthHost/AuthHost.csproj index 73babaf0..d4eb627c 100644 --- a/AuthHost/AuthHost.csproj +++ b/AuthHost/AuthHost.csproj @@ -36,6 +36,15 @@ 4 false + + bin\UnityRelease\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + ..\packages\Nancy.1.4.4\lib\net40\Nancy.dll diff --git a/ExampleApplication/ExampleApplication.csproj b/ExampleApplication/ExampleApplication.csproj index bc246543..cd4f0cd1 100644 --- a/ExampleApplication/ExampleApplication.csproj +++ b/ExampleApplication/ExampleApplication.csproj @@ -36,6 +36,15 @@ 4 false + + bin\UnityRelease\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll diff --git a/PusherClient.Tests.Utilities/PusherClient.Tests.Utilities.csproj b/PusherClient.Tests.Utilities/PusherClient.Tests.Utilities.csproj index 33c0446b..efa2a3bd 100644 --- a/PusherClient.Tests.Utilities/PusherClient.Tests.Utilities.csproj +++ b/PusherClient.Tests.Utilities/PusherClient.Tests.Utilities.csproj @@ -32,6 +32,15 @@ prompt 4 + + bin\UnityRelease\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll diff --git a/PusherClient.Tests/AcceptanceTests/EventEmitter.cs b/PusherClient.Tests/AcceptanceTests/EventEmitter.cs index 7a016996..fd633a1a 100644 --- a/PusherClient.Tests/AcceptanceTests/EventEmitter.cs +++ b/PusherClient.Tests/AcceptanceTests/EventEmitter.cs @@ -17,7 +17,7 @@ public void EventEmitterShouldEmitAnEventToARegisteredListener() var myAction = new Action(o => emittedEvent = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("listener event", myAction); // Act @@ -36,7 +36,7 @@ public void EventEmitterShouldEmitAnEventToARegisteredRawListener() var myAction = new Action(o => emittedEvent = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("raw listener event", myAction); // Act @@ -55,7 +55,7 @@ public void EventEmitterShouldEmitAnEventToARegisteredPusherEventListener() var myAction = new Action(o => emittedEvent = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("pusher event listener event", myAction); // Act @@ -75,7 +75,7 @@ public void EventEmitterShouldEmitAnEventToARegisteredGeneralListener() var myAction = new Action((e, o) => emittedEvent = new Tuple(e, o)); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.BindAll(myAction); // Act @@ -95,7 +95,7 @@ public void EventEmitterShouldEmitAnEventToARegisteredRawGeneralListener() var myAction = new Action((e, o) => emittedEvent = new Tuple(e, o)); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.BindAll(myAction); // Act @@ -115,7 +115,7 @@ public void EventEmitterShouldEmitAnEventToARegisteredPusherEventGeneralListener var myAction = new Action((e, o) => emittedEvent = new Tuple(e, o)); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.BindAll(myAction); // Act @@ -137,7 +137,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredListener() var myAction = new Action(o => emittedEvent = o); var myAction2 = new Action(o => emittedEvent2 = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("listener event", myAction); emitter.Bind("listener event", myAction2); emitter.Unbind("listener event", myAction); @@ -160,7 +160,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredRawListener() var myAction = new Action(o => emittedEvent = o); var myAction2 = new Action(o => emittedEvent2 = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("raw listener event", myAction); emitter.Bind("raw listener event", myAction2); emitter.Unbind("raw listener event", myAction); @@ -183,7 +183,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredPusherEventListener( var myAction = new Action(o => emittedEvent = o); var myAction2 = new Action(o => emittedEvent2 = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("pusher event listener event", myAction); emitter.Bind("pusher event listener event", myAction2); emitter.Unbind("pusher event listener event", myAction); @@ -206,7 +206,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredEventName() var myAction = new Action(o => emittedEvent = o); var myAction2 = new Action(o => emittedEvent2 = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("listener event", myAction); emitter.Bind("listener event", myAction2); emitter.Unbind("listener event"); @@ -229,7 +229,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredRawEventName() var myAction = new Action(o => emittedEvent = o); var myAction2 = new Action(o => emittedEvent2 = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("raw listener event", myAction); emitter.Bind("raw listener event", myAction2); emitter.Unbind("raw listener event"); @@ -252,7 +252,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredPusherEventEventName var myAction = new Action(o => emittedEvent = o); var myAction2 = new Action(o => emittedEvent2 = o); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.Bind("pusher event listener event", myAction); emitter.Bind("pusher event listener event", myAction2); emitter.Unbind("pusher event listener event"); @@ -273,7 +273,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredGeneralListener() var myAction = new Action((e, o) => emittedEvent = new Tuple(e, o)); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.BindAll(myAction); emitter.UnbindAll(); @@ -292,7 +292,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredRawGeneralListener() var myAction = new Action((e, o) => emittedEvent = new Tuple(e, o)); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.BindAll(myAction); emitter.UnbindAll(); @@ -311,7 +311,7 @@ public void EventEmitterShouldNotEmitAnEventToAnUnregisteredPusherEventGeneralLi var myAction = new Action((e, o) => emittedEvent = new Tuple(e, o)); - var emitter = new PusherClient.EventEmitter(); + var emitter = new PusherClient.EventEmitter(new NewtonsoftJsonSerializer()); emitter.BindAll(myAction); emitter.UnbindAll(); diff --git a/PusherClient.Tests/PusherClient.Tests.csproj b/PusherClient.Tests/PusherClient.Tests.csproj index 6f0d8862..b9857344 100644 --- a/PusherClient.Tests/PusherClient.Tests.csproj +++ b/PusherClient.Tests/PusherClient.Tests.csproj @@ -34,6 +34,15 @@ prompt 4 + + bin\UnityRelease\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + ..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll diff --git a/PusherClient/Channel.cs b/PusherClient/Channel.cs index f9bcc764..0578da15 100644 --- a/PusherClient/Channel.cs +++ b/PusherClient/Channel.cs @@ -32,6 +32,8 @@ internal Channel(string channelName, ITriggerChannels pusher) { _pusher = pusher; Name = channelName; + + JsonSerializer = pusher.JsonSerializer; } internal virtual void SubscriptionSucceeded(string data) diff --git a/PusherClient/Connection.cs b/PusherClient/Connection.cs index c24102a7..4e01fa4f 100644 --- a/PusherClient/Connection.cs +++ b/PusherClient/Connection.cs @@ -3,8 +3,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using WebSocket4Net; namespace PusherClient @@ -123,42 +121,30 @@ private void websocket_MessageReceived(object sender, MessageReceivedEventArgs e // bad: "{\"event\":\"pusher:error\",\"data\":{\"code\":4201,\"message\":\"Pong reply not received\"}}" // good: "{\"event\":\"pusher:error\",\"data\":\"{\\\"code\\\":4201,\\\"message\\\":\\\"Pong reply not received\\\"}\"}"; - var jObject = JObject.Parse(e.Message); - - if (jObject["data"] != null && jObject["data"].Type != JTokenType.String) - jObject["data"] = jObject["data"].ToString(Formatting.None); - - var jsonMessage = jObject.ToString(Formatting.None); - var template = new { @event = string.Empty, data = string.Empty, channel = string.Empty }; - - var message = JsonConvert.DeserializeAnonymousType(jsonMessage, template); - - var eventData = JsonConvert.DeserializeObject>(jsonMessage); - - if (jObject["data"] != null) - eventData["data"] = jObject["data"].ToString(Formatting.None); // undo any kind of deserialisation of the data property + var jsonMessage = _pusher.JsonSerializer.GetJsonWithStringProperty(e.Message, "data"); + var eventData = _pusher.JsonSerializer.Deserialize>(jsonMessage); var receivedEvent = new PusherEvent(eventData, jsonMessage); - _pusher.EmitPusherEvent(message.@event, receivedEvent); + _pusher.EmitPusherEvent(receivedEvent.EventName, receivedEvent); - if (message.@event.StartsWith(Constants.PUSHER_MESSAGE_PREFIX)) + if (receivedEvent.EventName.StartsWith(Constants.PUSHER_MESSAGE_PREFIX)) { // Assume Pusher event - switch (message.@event) + switch (receivedEvent.EventName) { // TODO - Need to handle Error on subscribing to a channel case Constants.ERROR: - ParseError(message.data); + ParseError(receivedEvent.Data); break; case Constants.CONNECTION_ESTABLISHED: - ParseConnectionEstablished(message.data); + ParseConnectionEstablished(receivedEvent.Data); break; case Constants.CHANNEL_SUBSCRIPTION_SUCCEEDED: - _pusher.SubscriptionSuceeded(message.channel, message.data); + _pusher.SubscriptionSuceeded(receivedEvent.ChannelName, receivedEvent.Data); break; case Constants.CHANNEL_SUBSCRIPTION_ERROR: @@ -166,21 +152,21 @@ private void websocket_MessageReceived(object sender, MessageReceivedEventArgs e break; case Constants.CHANNEL_MEMBER_ADDED: - _pusher.AddMember(message.channel, message.data); + _pusher.AddMember(receivedEvent.ChannelName, receivedEvent.Data); - Pusher.Trace.TraceEvent(TraceEventType.Warning, 0, "Received a presence event on channel '" + message.channel + "', however there is no presence channel which matches."); + Pusher.Trace.TraceEvent(TraceEventType.Warning, 0, "Received a presence event on channel '" + receivedEvent.ChannelName + "', however there is no presence channel which matches."); break; case Constants.CHANNEL_MEMBER_REMOVED: - _pusher.RemoveMember(message.channel, message.data); + _pusher.RemoveMember(receivedEvent.ChannelName, receivedEvent.Data); - Pusher.Trace.TraceEvent(TraceEventType.Warning, 0, "Received a presence event on channel '" + message.channel + "', however there is no presence channel which matches."); + Pusher.Trace.TraceEvent(TraceEventType.Warning, 0, "Received a presence event on channel '" + receivedEvent.ChannelName + "', however there is no presence channel which matches."); break; } } else // Assume channel event { - _pusher.EmitChannelEvent(message.channel, message.@event, receivedEvent); + _pusher.EmitChannelEvent(receivedEvent.ChannelName, receivedEvent.EventName, receivedEvent); } } @@ -239,19 +225,28 @@ private void websocket_Error(object sender, SuperSocket.ClientEngine.ErrorEventA } } + private class ConnectionEstablishedMessage + { + public string socket_id { get; set; } + } + private void ParseConnectionEstablished(string data) { - var template = new { socket_id = string.Empty }; - var message = JsonConvert.DeserializeAnonymousType(data, template); + var message = _pusher.JsonSerializer.Deserialize(data); SocketId = message.socket_id; ChangeState(ConnectionState.Connected); } + private class ErrorMessage + { + public string message { get; set; } + public int? code { get; set; } + } + private void ParseError(string data) { - var template = new { message = string.Empty, code = (int?) null }; - var parsed = JsonConvert.DeserializeAnonymousType(data, template); + var parsed = _pusher.JsonSerializer.Deserialize(data); ErrorCodes error = ErrorCodes.Unkown; diff --git a/PusherClient/ErrorConstants.cs b/PusherClient/ErrorConstants.cs index 40d73e3e..ff0e4317 100644 --- a/PusherClient/ErrorConstants.cs +++ b/PusherClient/ErrorConstants.cs @@ -4,5 +4,6 @@ class ErrorConstants { public const string ApplicationKeyNotSet = "The application key cannot be null or whitespace"; public const string ConnectionAlreadyConnected = "Attempt to connect when another connection has already started. New attempt has been ignored."; + public const string JsonSerializerNotProvided = "The options used for initialization must contain a value for JsonSerializer."; } } diff --git a/PusherClient/EventEmitter.cs b/PusherClient/EventEmitter.cs index 71cf079f..38e328b9 100644 --- a/PusherClient/EventEmitter.cs +++ b/PusherClient/EventEmitter.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; namespace PusherClient { /// /// Class used to Bind and unbind from events /// - public class EventEmitter + public class EventEmitter : IRequiresJsonSerializer { private readonly Dictionary>> _eventListeners = new Dictionary>>(); private readonly List> _generalListeners = new List>(); @@ -19,6 +18,18 @@ public class EventEmitter private readonly Dictionary>> _pusherEventEventListeners = new Dictionary>>(); private readonly List> _pusherEventGeneralListeners = new List>(); + public IJsonSerializer JsonSerializer { get; protected set; } + + public EventEmitter() + { + + } + + internal EventEmitter(IJsonSerializer serializer) + { + JsonSerializer = serializer; + } + /// /// Binds to a given event name /// @@ -189,7 +200,7 @@ internal void EmitDynamicEvent(string eventName, string data) { if (_generalListeners.Count > 0 || _eventListeners.Count > 0) { - var dynamicData = JsonConvert.DeserializeObject(data); + var dynamicData = JsonSerializer.Deserialize(data); ActionData(_generalListeners, _eventListeners, eventName, dynamicData); } } diff --git a/PusherClient/GenericPresenceChannel.cs b/PusherClient/GenericPresenceChannel.cs index 19fba036..ab73c6ca 100644 --- a/PusherClient/GenericPresenceChannel.cs +++ b/PusherClient/GenericPresenceChannel.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Collections.Generic; -using Newtonsoft.Json; namespace PusherClient { @@ -75,7 +74,7 @@ private ConcurrentDictionary ParseMembersList(string data) { ConcurrentDictionary members = new ConcurrentDictionary(); - var dataAsObj = JsonConvert.DeserializeObject(data); + var dataAsObj = JsonSerializer.Deserialize(data); for (int i = 0; i < (int)dataAsObj.presence.count; i++) { @@ -95,7 +94,7 @@ private class MemberData private KeyValuePair ParseMember(string data) { - var dataAsObj = JsonConvert.DeserializeObject(data); + var dataAsObj = JsonSerializer.Deserialize(data); var id = dataAsObj.user_id; var val = dataAsObj.user_info; diff --git a/PusherClient/IJsonSerializer.cs b/PusherClient/IJsonSerializer.cs new file mode 100644 index 00000000..076eae62 --- /dev/null +++ b/PusherClient/IJsonSerializer.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PusherClient +{ + public interface IJsonSerializer + { + string Serialize(T obj); + T Deserialize(string json); + + string GetJsonWithStringProperty(string json, string property); + } +} diff --git a/PusherClient/IPusher.cs b/PusherClient/IPusher.cs index 6d1650da..bb211563 100644 --- a/PusherClient/IPusher.cs +++ b/PusherClient/IPusher.cs @@ -1,8 +1,6 @@ -using System.Collections.Generic; - -namespace PusherClient +namespace PusherClient { - internal interface IPusher + internal interface IPusher : IRequiresJsonSerializer { void ConnectionStateChanged(ConnectionState state); void ErrorOccured(PusherException pusherException); diff --git a/PusherClient/IRequiresJsonSerializer.cs b/PusherClient/IRequiresJsonSerializer.cs new file mode 100644 index 00000000..11958a98 --- /dev/null +++ b/PusherClient/IRequiresJsonSerializer.cs @@ -0,0 +1,7 @@ +namespace PusherClient +{ + public interface IRequiresJsonSerializer + { + IJsonSerializer JsonSerializer { get; } + } +} diff --git a/PusherClient/ITriggerChannels.cs b/PusherClient/ITriggerChannels.cs index 2348767e..6f4b1418 100644 --- a/PusherClient/ITriggerChannels.cs +++ b/PusherClient/ITriggerChannels.cs @@ -2,7 +2,7 @@ namespace PusherClient { - internal interface ITriggerChannels + internal interface ITriggerChannels : IRequiresJsonSerializer { Task Trigger(string channelName, string eventName, object obj); diff --git a/PusherClient/NewtonsoftJsonSerializer.cs b/PusherClient/NewtonsoftJsonSerializer.cs new file mode 100644 index 00000000..db969d40 --- /dev/null +++ b/PusherClient/NewtonsoftJsonSerializer.cs @@ -0,0 +1,30 @@ +#if !NO_NEWTONSOFT_JSON +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PusherClient +{ + internal class NewtonsoftJsonSerializer : IJsonSerializer + { + public T Deserialize(string json) + { + return JsonConvert.DeserializeObject(json); + } + + public string Serialize(T obj) + { + return JsonConvert.SerializeObject(obj); + } + + public string GetJsonWithStringProperty(string json, string property) + { + var jObject = JObject.Parse(json); + + if (jObject[property] != null && jObject[property].Type != JTokenType.String) + jObject[property] = jObject[property].ToString(); + + return jObject.ToString(Formatting.None); + } + } +} +#endif \ No newline at end of file diff --git a/PusherClient/PresenceChannel.cs b/PusherClient/PresenceChannel.cs index c6a31d86..450f8177 100644 --- a/PusherClient/PresenceChannel.cs +++ b/PusherClient/PresenceChannel.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Newtonsoft.Json; namespace PusherClient { diff --git a/PusherClient/Pusher.cs b/PusherClient/Pusher.cs index 9545ca2b..73d33778 100644 --- a/PusherClient/Pusher.cs +++ b/PusherClient/Pusher.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; namespace PusherClient { @@ -98,6 +97,19 @@ public Pusher(string applicationKey, PusherOptions options = null) _applicationKey = applicationKey; _options = options ?? new PusherOptions { Encrypted = false }; + + if (options.JsonSerializer == null) + { +#if NO_NEWTONSOFT_JSON + throw new ArgumentException(ErrorConstants.JsonSerializerNotProvided, nameof(options)); +#else + JsonSerializer = new NewtonsoftJsonSerializer(); +#endif + } + else + { + JsonSerializer = options.JsonSerializer; + } } void IPusher.ConnectionStateChanged(ConnectionState state) @@ -296,6 +308,12 @@ public async Task> SubscribePresenceAsync SubscribeToChannel(string channelName) { var channelType = GetChannelType(channelName); @@ -309,15 +327,14 @@ private async Task SubscribeToChannel(string channelName) { var jsonAuth = _options.Authorizer.Authorize(channelName, _connection.SocketId); - var template = new { auth = string.Empty, channel_data = string.Empty }; - var message = JsonConvert.DeserializeAnonymousType(jsonAuth, template); + var message = JsonSerializer.Deserialize(jsonAuth); - await _connection.Send(JsonConvert.SerializeObject(new { @event = Constants.CHANNEL_SUBSCRIBE, data = new { channel = channelName, auth = message.auth, channel_data = message.channel_data } })); + await _connection.Send(JsonSerializer.Serialize(new { @event = Constants.CHANNEL_SUBSCRIBE, data = new { channel = channelName, auth = message.auth, channel_data = message.channel_data } })); } else { // No need for auth details. Just send subscribe event - await _connection.Send(JsonConvert.SerializeObject(new { @event = Constants.CHANNEL_SUBSCRIBE, data = new { channel = channelName } })); + await _connection.Send(JsonSerializer.Serialize(new { @event = Constants.CHANNEL_SUBSCRIBE, data = new { channel = channelName } })); } } @@ -377,14 +394,14 @@ private void AuthEndpointCheck() async Task ITriggerChannels.Trigger(string channelName, string eventName, object obj) { - await _connection.Send(JsonConvert.SerializeObject(new { @event = eventName, channel = channelName, data = obj })); + await _connection.Send(JsonSerializer.Serialize(new { @event = eventName, channel = channelName, data = obj })); } async Task ITriggerChannels.Unsubscribe(string channelName) { if (_connection.IsConnected) { - await _connection.Send(JsonConvert.SerializeObject(new + await _connection.Send(JsonSerializer.Serialize(new { @event = Constants.CHANNEL_UNSUBSCRIBE, data = new {channel = channelName} diff --git a/PusherClient/PusherClient.csproj b/PusherClient/PusherClient.csproj index be35c4a8..0e189ae8 100644 --- a/PusherClient/PusherClient.csproj +++ b/PusherClient/PusherClient.csproj @@ -2,6 +2,7 @@ net45;net46;netstandard1.6 + Debug;Release;UnityRelease @@ -17,9 +18,12 @@ https://pusher.com/static_logos/64x64.png + + NO_NEWTONSOFT_JSON + + - @@ -27,6 +31,12 @@ + + + 11.0.2 + + + 4.3.0 diff --git a/PusherClient/PusherOptions.cs b/PusherClient/PusherOptions.cs index 7ff9df97..faf7a3fb 100644 --- a/PusherClient/PusherOptions.cs +++ b/PusherClient/PusherOptions.cs @@ -21,5 +21,10 @@ public class PusherOptions public string Cluster { get; set; } = "mt1"; internal string Host => $"ws-{Cluster}.pusher.com"; + + /// + /// Interface used for internal JSON serialization + /// + public IJsonSerializer JsonSerializer { get; set; } } } \ No newline at end of file diff --git a/pusher-dotnet-client.sln b/pusher-dotnet-client.sln index 1b5bf80f..d461a258 100644 --- a/pusher-dotnet-client.sln +++ b/pusher-dotnet-client.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2042 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28917.181 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleApplication", "ExampleApplication\ExampleApplication.csproj", "{D6D38D9A-EF72-4C0D-A4A9-C325775E5CA3}" EndProject @@ -24,28 +24,39 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + UnityRelease|Any CPU = UnityRelease|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D6D38D9A-EF72-4C0D-A4A9-C325775E5CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D6D38D9A-EF72-4C0D-A4A9-C325775E5CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {D6D38D9A-EF72-4C0D-A4A9-C325775E5CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {D6D38D9A-EF72-4C0D-A4A9-C325775E5CA3}.Release|Any CPU.Build.0 = Release|Any CPU + {D6D38D9A-EF72-4C0D-A4A9-C325775E5CA3}.UnityRelease|Any CPU.ActiveCfg = UnityRelease|Any CPU + {D6D38D9A-EF72-4C0D-A4A9-C325775E5CA3}.UnityRelease|Any CPU.Build.0 = UnityRelease|Any CPU {218EE921-5E27-4E15-9382-42DD2F40857C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {218EE921-5E27-4E15-9382-42DD2F40857C}.Debug|Any CPU.Build.0 = Debug|Any CPU {218EE921-5E27-4E15-9382-42DD2F40857C}.Release|Any CPU.ActiveCfg = Release|Any CPU {218EE921-5E27-4E15-9382-42DD2F40857C}.Release|Any CPU.Build.0 = Release|Any CPU + {218EE921-5E27-4E15-9382-42DD2F40857C}.UnityRelease|Any CPU.ActiveCfg = UnityRelease|Any CPU + {218EE921-5E27-4E15-9382-42DD2F40857C}.UnityRelease|Any CPU.Build.0 = UnityRelease|Any CPU {F016BFD6-1BD2-4E30-A2C2-1A02A85C0478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F016BFD6-1BD2-4E30-A2C2-1A02A85C0478}.Debug|Any CPU.Build.0 = Debug|Any CPU {F016BFD6-1BD2-4E30-A2C2-1A02A85C0478}.Release|Any CPU.ActiveCfg = Release|Any CPU {F016BFD6-1BD2-4E30-A2C2-1A02A85C0478}.Release|Any CPU.Build.0 = Release|Any CPU + {F016BFD6-1BD2-4E30-A2C2-1A02A85C0478}.UnityRelease|Any CPU.ActiveCfg = UnityRelease|Any CPU + {F016BFD6-1BD2-4E30-A2C2-1A02A85C0478}.UnityRelease|Any CPU.Build.0 = UnityRelease|Any CPU {00F1F3BE-1210-4D06-89CA-F1E17898384C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {00F1F3BE-1210-4D06-89CA-F1E17898384C}.Debug|Any CPU.Build.0 = Debug|Any CPU {00F1F3BE-1210-4D06-89CA-F1E17898384C}.Release|Any CPU.ActiveCfg = Release|Any CPU {00F1F3BE-1210-4D06-89CA-F1E17898384C}.Release|Any CPU.Build.0 = Release|Any CPU + {00F1F3BE-1210-4D06-89CA-F1E17898384C}.UnityRelease|Any CPU.ActiveCfg = UnityRelease|Any CPU + {00F1F3BE-1210-4D06-89CA-F1E17898384C}.UnityRelease|Any CPU.Build.0 = UnityRelease|Any CPU {A325BB9F-6476-4422-AEF4-C22FA53890DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A325BB9F-6476-4422-AEF4-C22FA53890DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {A325BB9F-6476-4422-AEF4-C22FA53890DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {A325BB9F-6476-4422-AEF4-C22FA53890DD}.Release|Any CPU.Build.0 = Release|Any CPU + {A325BB9F-6476-4422-AEF4-C22FA53890DD}.UnityRelease|Any CPU.ActiveCfg = UnityRelease|Any CPU + {A325BB9F-6476-4422-AEF4-C22FA53890DD}.UnityRelease|Any CPU.Build.0 = UnityRelease|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From c4817e8bcc8aa5c987b490aa74970dcfcbeb9466 Mon Sep 17 00:00:00 2001 From: Collin Dauphinee Date: Wed, 6 Nov 2019 23:26:30 +0000 Subject: [PATCH 2/3] Fixing issues with Unity stripping message models --- PusherClient/Connection.cs | 12 ++---------- PusherClient/Messages/AuthMessage.cs | 11 +++++++++++ .../Messages/ConnectionEstablishedMessage.cs | 9 +++++++++ PusherClient/Messages/ErrorMessage.cs | 11 +++++++++++ PusherClient/PreserveAttribute.cs | 10 ++++++++++ PusherClient/Pusher.cs | 7 +------ 6 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 PusherClient/Messages/AuthMessage.cs create mode 100644 PusherClient/Messages/ConnectionEstablishedMessage.cs create mode 100644 PusherClient/Messages/ErrorMessage.cs create mode 100644 PusherClient/PreserveAttribute.cs diff --git a/PusherClient/Connection.cs b/PusherClient/Connection.cs index 4e01fa4f..b16db4cf 100644 --- a/PusherClient/Connection.cs +++ b/PusherClient/Connection.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using WebSocket4Net; +using PusherClient.Messages; namespace PusherClient { @@ -225,11 +226,6 @@ private void websocket_Error(object sender, SuperSocket.ClientEngine.ErrorEventA } } - private class ConnectionEstablishedMessage - { - public string socket_id { get; set; } - } - private void ParseConnectionEstablished(string data) { var message = _pusher.JsonSerializer.Deserialize(data); @@ -238,11 +234,7 @@ private void ParseConnectionEstablished(string data) ChangeState(ConnectionState.Connected); } - private class ErrorMessage - { - public string message { get; set; } - public int? code { get; set; } - } + private void ParseError(string data) { diff --git a/PusherClient/Messages/AuthMessage.cs b/PusherClient/Messages/AuthMessage.cs new file mode 100644 index 00000000..3aede424 --- /dev/null +++ b/PusherClient/Messages/AuthMessage.cs @@ -0,0 +1,11 @@ +namespace PusherClient.Messages +{ + [Preserve] + public class AuthMessage + { + [Preserve] + public string auth { get; set; } + [Preserve] + public string channel_data { get; set; } + } +} diff --git a/PusherClient/Messages/ConnectionEstablishedMessage.cs b/PusherClient/Messages/ConnectionEstablishedMessage.cs new file mode 100644 index 00000000..67cb90dd --- /dev/null +++ b/PusherClient/Messages/ConnectionEstablishedMessage.cs @@ -0,0 +1,9 @@ +namespace PusherClient.Messages +{ + [Preserve] + public class ConnectionEstablishedMessage + { + [Preserve] + public string socket_id { get; set; } + } +} diff --git a/PusherClient/Messages/ErrorMessage.cs b/PusherClient/Messages/ErrorMessage.cs new file mode 100644 index 00000000..7b5a7a7f --- /dev/null +++ b/PusherClient/Messages/ErrorMessage.cs @@ -0,0 +1,11 @@ +namespace PusherClient.Messages +{ + [Preserve] + public class ErrorMessage + { + [Preserve] + public string message { get; set; } + [Preserve] + public int? code { get; set; } + } +} diff --git a/PusherClient/PreserveAttribute.cs b/PusherClient/PreserveAttribute.cs new file mode 100644 index 00000000..24cd1a42 --- /dev/null +++ b/PusherClient/PreserveAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace PusherClient +{ + // This attribute is required to prevent il2cpp from stripping models used for serialization + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] + public class PreserveAttribute : Attribute + { + } +} diff --git a/PusherClient/Pusher.cs b/PusherClient/Pusher.cs index 73d33778..589b5b99 100644 --- a/PusherClient/Pusher.cs +++ b/PusherClient/Pusher.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using PusherClient.Messages; namespace PusherClient { @@ -308,12 +309,6 @@ public async Task> SubscribePresenceAsync SubscribeToChannel(string channelName) { var channelType = GetChannelType(channelName); From e9e7f0ee45a1722732a89d1c236c76968bf100b5 Mon Sep 17 00:00:00 2001 From: Collin Dauphinee Date: Wed, 15 Jan 2020 23:34:52 +0000 Subject: [PATCH 3/3] Replacing websocket library --- PusherClient/Connection.cs | 57 +++++++++++++++++++------------- PusherClient/PusherClient.csproj | 14 ++++++-- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/PusherClient/Connection.cs b/PusherClient/Connection.cs index b16db4cf..cfd70857 100644 --- a/PusherClient/Connection.cs +++ b/PusherClient/Connection.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using WebSocket4Net; +using WebSocketSharp; using PusherClient.Messages; namespace PusherClient @@ -11,6 +11,7 @@ namespace PusherClient internal class Connection { private WebSocket _websocket; + private Timer _pingTimer; private readonly string _url; private readonly IPusher _pusher; @@ -53,18 +54,14 @@ internal Task Connect() ChangeState(ConnectionState.Initialized); _allowReconnect = true; - _websocket = new WebSocket(_url) - { - EnableAutoSendPing = true, - AutoSendPingInterval = 1 - }; + _websocket = new WebSocket(_url); - _websocket.Opened += websocket_Opened; - _websocket.Error += websocket_Error; - _websocket.Closed += websocket_Closed; - _websocket.MessageReceived += websocket_MessageReceived; + _websocket.OnOpen += websocket_Opened; + _websocket.OnError += websocket_Error; + _websocket.OnClose += websocket_Closed; + _websocket.OnMessage += websocket_MessageReceived; - _websocket.Open(); + _websocket.ConnectAsync(); return completionSource.Task; } @@ -109,11 +106,11 @@ internal async Task Send(string message) return false; } - private void websocket_MessageReceived(object sender, MessageReceivedEventArgs e) + private void websocket_MessageReceived(object sender, MessageEventArgs e) { - Pusher.Trace.TraceEvent(TraceEventType.Information, 0, "Websocket message received: " + e.Message); + Pusher.Trace.TraceEvent(TraceEventType.Information, 0, "Websocket message received: " + e.Data); - Debug.WriteLine(e.Message); + Debug.WriteLine(e.Data); // DeserializeAnonymousType will throw and error when an error comes back from pusher // It stems from the fact that the data object is a string normally except when an error is sent back @@ -122,7 +119,7 @@ private void websocket_MessageReceived(object sender, MessageReceivedEventArgs e // bad: "{\"event\":\"pusher:error\",\"data\":{\"code\":4201,\"message\":\"Pong reply not received\"}}" // good: "{\"event\":\"pusher:error\",\"data\":\"{\\\"code\\\":4201,\\\"message\\\":\\\"Pong reply not received\\\"}\"}"; - var jsonMessage = _pusher.JsonSerializer.GetJsonWithStringProperty(e.Message, "data"); + var jsonMessage = _pusher.JsonSerializer.GetJsonWithStringProperty(e.Data, "data"); var eventData = _pusher.JsonSerializer.Deserialize>(jsonMessage); var receivedEvent = new PusherEvent(eventData, jsonMessage); @@ -149,7 +146,7 @@ private void websocket_MessageReceived(object sender, MessageReceivedEventArgs e break; case Constants.CHANNEL_SUBSCRIPTION_ERROR: - RaiseError(new PusherException("Error received on channel subscriptions: " + e.Message, ErrorCodes.SubscriptionError)); + RaiseError(new PusherException("Error received on channel subscriptions: " + e.Data, ErrorCodes.SubscriptionError)); break; case Constants.CHANNEL_MEMBER_ADDED: @@ -176,20 +173,34 @@ private void websocket_Opened(object sender, EventArgs e) Pusher.Trace.TraceEvent(TraceEventType.Information, 0, "Websocket opened OK."); _connectionTaskComplete.SetResult(ConnectionState.Connected); _connectionTaskComplete = null; + + // We need to manually ping, otherwise the server will time out the connection. + _pingTimer = new Timer(DoPing, _websocket, 60000, 60000); } - private void websocket_Closed(object sender, EventArgs e) + private void DoPing(object state) + { + var socket = state as WebSocket; + if (socket == null || !socket.IsAlive || socket.ReadyState != WebSocketState.Open) + return; + + socket.Ping(); + } + + private void websocket_Closed(object sender, CloseEventArgs e) { Pusher.Trace.TraceEvent(TraceEventType.Warning, 0, "Websocket connection has been closed"); - _websocket.Opened -= websocket_Opened; - _websocket.Error -= websocket_Error; - _websocket.Closed -= websocket_Closed; - _websocket.MessageReceived -= websocket_MessageReceived; + _websocket.OnOpen -= websocket_Opened; + _websocket.OnError -= websocket_Error; + _websocket.OnClose -= websocket_Closed; + _websocket.OnMessage -= websocket_MessageReceived; + + _pingTimer.Dispose(); if (_websocket != null) { - _websocket.Dispose(); + ((IDisposable)_websocket).Dispose(); _websocket = null; } @@ -211,7 +222,7 @@ private void websocket_Closed(object sender, EventArgs e) } } - private void websocket_Error(object sender, SuperSocket.ClientEngine.ErrorEventArgs e) + private void websocket_Error(object sender, ErrorEventArgs e) { Pusher.Trace.TraceEvent(TraceEventType.Error, 0, "Error: " + e.Exception); diff --git a/PusherClient/PusherClient.csproj b/PusherClient/PusherClient.csproj index 0e189ae8..6383ebe6 100644 --- a/PusherClient/PusherClient.csproj +++ b/PusherClient/PusherClient.csproj @@ -1,14 +1,14 @@  - net45;net46;netstandard1.6 + net45;net46 Debug;Release;UnityRelease PusherClient Pusher .NET Client Library - 1.1.0 + 1.1.1 false pusher realtime websocket https://github.com/pusher/pusher-websocket-dotnet/blob/master/LICENSE.txt @@ -24,7 +24,6 @@ - @@ -41,6 +40,9 @@ 4.3.0 + + 1.0.4 + @@ -49,4 +51,10 @@ + + + 1.0.4 + + +