Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
name: c-sharp
version: "8.0.3"
version: "8.0.4"
schema: 1
scm: github.com/pubnub/c-sharp
changelog:
- date: 2025-12-04
version: v8.0.4
changes:
- type: improvement
text: "Prevent resubscribe to previously subscribed entities."
- date: 2025-11-20
version: v8.0.3
changes:
Expand Down Expand Up @@ -967,7 +972,7 @@ features:
- QUERY-PARAM
supported-platforms:
-
version: Pubnub 'C#' 8.0.3
version: Pubnub 'C#' 8.0.4
platforms:
- Windows 10 and up
- Windows Server 2008 and up
Expand All @@ -978,7 +983,7 @@ supported-platforms:
- .Net Framework 4.6.1+
- .Net Framework 6.0
-
version: PubnubPCL 'C#' 8.0.3
version: PubnubPCL 'C#' 8.0.4
platforms:
- Xamarin.Android
- Xamarin.iOS
Expand All @@ -998,7 +1003,7 @@ supported-platforms:
- .Net Core
- .Net 6.0
-
version: PubnubUWP 'C#' 8.0.3
version: PubnubUWP 'C#' 8.0.4
platforms:
- Windows Phone 10
- Universal Windows Apps
Expand All @@ -1022,7 +1027,7 @@ sdks:
distribution-type: source
distribution-repository: GitHub
package-name: Pubnub
location: https://github.com/pubnub/c-sharp/releases/tag/v8.0.3
location: https://github.com/pubnub/c-sharp/releases/tag/v8.0.4
requires:
-
name: ".Net"
Expand Down Expand Up @@ -1305,7 +1310,7 @@ sdks:
distribution-type: source
distribution-repository: GitHub
package-name: PubNubPCL
location: https://github.com/pubnub/c-sharp/releases/tag/v8.0.3
location: https://github.com/pubnub/c-sharp/releases/tag/v8.0.4
requires:
-
name: ".Net Core"
Expand Down Expand Up @@ -1664,7 +1669,7 @@ sdks:
distribution-type: source
distribution-repository: GitHub
package-name: PubnubUWP
location: https://github.com/pubnub/c-sharp/releases/tag/v8.0.3
location: https://github.com/pubnub/c-sharp/releases/tag/v8.0.4
requires:
-
name: "Universal Windows Platform Development"
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
v8.0.4 - December 04 2025
-----------------------------
- Modified: prevent resubscribe to previously subscribed entities.

v8.0.3 - November 20 2025
-----------------------------
- Modified: refactor EmitStatus to prevent concurrent modification issue resulting InvalidOperationException.
Expand Down
383 changes: 217 additions & 166 deletions src/Api/PubnubApi/EventEngine/Common/EventEmitter.cs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/Api/PubnubApi/EventEngine/Core/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ private async Task Transition(IEvent e)
return;
}

if (stateInvocationPair.State is null)
{
logger?.Warn($"TransitionResult returned null State for event {e?.GetType()?.Name}. Ignoring transition.");
return;
}

await ExecuteStateChange(currentState, stateInvocationPair.State, stateInvocationPair.Invocations).ConfigureAwait(false);

this.currentState = stateInvocationPair.State;
Expand Down
55 changes: 34 additions & 21 deletions src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System;
using PubnubApi.EventEngine.Subscribe.Events;
using PubnubApi.EventEngine.Common;
using PubnubApi.EventEngine.Subscribe.Effects;
using PubnubApi.EventEngine.Subscribe.Invocations;

namespace PubnubApi.EventEngine.Subscribe
{
Expand All @@ -18,6 +20,7 @@ public class SubscribeEventEngine : Engine

public List<string> Channels { get; } = [];
public List<string> ChannelGroups { get; } = [];
private readonly EmitStatusEffectHandler emitStatusHandler;

internal SubscribeEventEngine(Pubnub pubnubInstance,
PNConfiguration pubnubConfiguration,
Expand All @@ -28,33 +31,37 @@ internal SubscribeEventEngine(Pubnub pubnubInstance,
{
this.subscribeManager = subscribeManager;
this.jsonPluggableLibrary = jsonPluggableLibrary;
var handshakeHandler = new Effects.HandshakeEffectHandler(subscribeManager, EventQueue);
var handshakeReconnectHandler = new Effects.HandshakeReconnectEffectHandler(pubnubConfiguration, EventQueue, handshakeHandler);
var handshakeHandler = new HandshakeEffectHandler(subscribeManager, EventQueue);
var handshakeReconnectHandler = new HandshakeReconnectEffectHandler(pubnubConfiguration, EventQueue, handshakeHandler);

dispatcher.Register<Invocations.HandshakeInvocation, Effects.HandshakeEffectHandler>(handshakeHandler);
dispatcher.Register<Invocations.CancelHandshakeInvocation, Effects.HandshakeEffectHandler>(handshakeHandler);
dispatcher.Register<Invocations.HandshakeReconnectInvocation, Effects.HandshakeReconnectEffectHandler>(handshakeReconnectHandler);
dispatcher.Register<Invocations.CancelHandshakeReconnectInvocation, Effects.HandshakeReconnectEffectHandler>(handshakeReconnectHandler);
dispatcher.Register<HandshakeInvocation, HandshakeEffectHandler>(handshakeHandler);
dispatcher.Register<CancelHandshakeInvocation, HandshakeEffectHandler>(handshakeHandler);
dispatcher.Register<HandshakeReconnectInvocation, HandshakeReconnectEffectHandler>(handshakeReconnectHandler);
dispatcher.Register<CancelHandshakeReconnectInvocation, HandshakeReconnectEffectHandler>(handshakeReconnectHandler);

var receiveHandler = new Effects.ReceivingEffectHandler(subscribeManager, EventQueue);
var receiveReconnectHandler = new Effects.ReceivingReconnectEffectHandler(pubnubConfiguration, EventQueue, receiveHandler);
var receiveHandler = new ReceivingEffectHandler(subscribeManager, EventQueue);
var receiveReconnectHandler = new ReceivingReconnectEffectHandler(pubnubConfiguration, EventQueue, receiveHandler);

dispatcher.Register<Invocations.ReceiveMessagesInvocation, Effects.ReceivingEffectHandler>(receiveHandler);
dispatcher.Register<Invocations.CancelReceiveMessagesInvocation, Effects.ReceivingEffectHandler>(receiveHandler);
dispatcher.Register<Invocations.ReceiveReconnectInvocation, Effects.ReceivingReconnectEffectHandler>(receiveReconnectHandler);
dispatcher.Register<Invocations.CancelReceiveReconnectInvocation, Effects.ReceivingReconnectEffectHandler>(receiveReconnectHandler);
dispatcher.Register<ReceiveMessagesInvocation, ReceivingEffectHandler>(receiveHandler);
dispatcher.Register<CancelReceiveMessagesInvocation, ReceivingEffectHandler>(receiveHandler);
dispatcher.Register<ReceiveReconnectInvocation, ReceivingReconnectEffectHandler>(receiveReconnectHandler);
dispatcher.Register<CancelReceiveReconnectInvocation, ReceivingReconnectEffectHandler>(receiveReconnectHandler);

var emitMessageHandler = new Effects.EmitMessagesHandler(eventEmitter, jsonPluggableLibrary, channelTypeMap, channelGroupTypeMap);
dispatcher.Register<Invocations.EmitMessagesInvocation, Effects.EmitMessagesHandler>(emitMessageHandler);
var emitMessageHandler = new EmitMessagesHandler(eventEmitter, jsonPluggableLibrary, channelTypeMap, channelGroupTypeMap);
dispatcher.Register<EmitMessagesInvocation, EmitMessagesHandler>(emitMessageHandler);

var emitStatusHandler = new Effects.EmitStatusEffectHandler(pubnubInstance, statusListener);
dispatcher.Register<Invocations.EmitStatusInvocation, Effects.EmitStatusEffectHandler>(emitStatusHandler);
emitStatusHandler = new EmitStatusEffectHandler(pubnubInstance, statusListener);
dispatcher.Register<EmitStatusInvocation, EmitStatusEffectHandler>(emitStatusHandler);

currentState = new UnsubscribedState();
logger = pubnubConfiguration.Logger;
}
public void Subscribe<T>(string[] channels, string[] channelGroups, SubscriptionCursor cursor)
{
bool allChannelsExist = channels.All(c => Channels.Contains(c));
bool allChannelGroupsExist = channelGroups.All(cg => ChannelGroups.Contains(cg));
bool isNewSubscription = !(allChannelsExist && allChannelGroupsExist);

Channels.AddRange(channels);
ChannelGroups.AddRange(channelGroups);

Expand All @@ -71,10 +78,16 @@ public void Subscribe<T>(string[] channels, string[] channelGroups, Subscription
Cursor = cursor
});
} else {
EventQueue.Enqueue(new SubscriptionChangedEvent() {
Channels = Channels.Distinct(),
ChannelGroups = ChannelGroups.Distinct()
});
if (isNewSubscription) {
EventQueue.Enqueue(new SubscriptionChangedEvent() {
Channels = Channels.Distinct(),
ChannelGroups = ChannelGroups.Distinct()
});
} else {
var status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNSubscriptionChangedCategory, Channels, ChannelGroups, Constants.HttpRequestSuccessStatusCode);
var invocation = new EmitStatusInvocation(status);
_ = emitStatusHandler.Run(invocation);
}
}
}

Expand Down Expand Up @@ -104,4 +117,4 @@ public void Unsubscribe(string[] channels, string[] channelGroups)
}
}
}
}
}
16 changes: 15 additions & 1 deletion src/Api/PubnubApi/Helper/MobilePushHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public Dictionary<string, object> GetPayload()
Dictionary<string, object> pnFcm = BuildFcmPayload(pushType);
if (pnFcm != null)
{
ret.Add("pn_gcm", pnFcm);
ret.Add("pn_fcm", pnFcm);
}
}
}
Expand Down Expand Up @@ -248,6 +248,10 @@ public class Apns2Data
{
public string collapseId { get; set; }
public string expiration { get; set; }

[JsonConverter(typeof(StringEnumConverter))]
public APNS2AuthMethod authMethod { get; set; }

public List<PushTarget> targets { get; set; }

public string version { get; } = "v2";
Expand All @@ -262,6 +266,16 @@ public class PushTarget
public Environment environment { get; set; }
}

public enum APNS2AuthMethod
{
[EnumMember(Value = "token")]
TOKEN,
[EnumMember(Value = "cert")]
CERT,
[EnumMember(Value = "certificate")]
CERTIFICATE
}

public enum Environment
{
[EnumMember(Value = "development")]
Expand Down
1 change: 0 additions & 1 deletion src/Api/PubnubApi/Interface/IJsonPluggableLibrary.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;

namespace PubnubApi
{
Expand Down
4 changes: 2 additions & 2 deletions src/Api/PubnubApi/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
[assembly: AssemblyProduct("Pubnub C# SDK")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("8.0.3")]
[assembly: AssemblyFileVersion("8.0.3")]
[assembly: AssemblyVersion("8.0.4")]
[assembly: AssemblyFileVersion("8.0.4")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
Expand Down
4 changes: 2 additions & 2 deletions src/Api/PubnubApi/PubnubApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@

<PropertyGroup>
<PackageId>Pubnub</PackageId>
<PackageVersion>8.0.3</PackageVersion>
<PackageVersion>8.0.4</PackageVersion>
<Title>PubNub C# .NET - Web Data Push API</Title>
<Authors>Pandu Masabathula</Authors>
<Owners>PubNub</Owners>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageIconUrl>http://pubnub.s3.amazonaws.com/2011/powered-by-pubnub/pubnub-icon-600x600.png</PackageIconUrl>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RepositoryUrl>https://github.com/pubnub/c-sharp/</RepositoryUrl>
<PackageReleaseNotes>Refactor EmitStatus to prevent concurrent modification issue resulting InvalidOperationException.</PackageReleaseNotes>
<PackageReleaseNotes>Prevent resubscribe to previously subscribed entities.</PackageReleaseNotes>
<PackageTags>Web Data Push Real-time Notifications ESB Message Broadcasting Distributed Computing</PackageTags>
<!--<Summary>PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously</Summary>-->
<Description>PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously</Description>
Expand Down
4 changes: 2 additions & 2 deletions src/Api/PubnubApiPCL/PubnubApiPCL.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@

<PropertyGroup>
<PackageId>PubnubPCL</PackageId>
<PackageVersion>8.0.3</PackageVersion>
<PackageVersion>8.0.4</PackageVersion>
<Title>PubNub C# .NET - Web Data Push API</Title>
<Authors>Pandu Masabathula</Authors>
<Owners>PubNub</Owners>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageIconUrl>http://pubnub.s3.amazonaws.com/2011/powered-by-pubnub/pubnub-icon-600x600.png</PackageIconUrl>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RepositoryUrl>https://github.com/pubnub/c-sharp/</RepositoryUrl>
<PackageReleaseNotes>Refactor EmitStatus to prevent concurrent modification issue resulting InvalidOperationException.</PackageReleaseNotes>
<PackageReleaseNotes>Prevent resubscribe to previously subscribed entities.</PackageReleaseNotes>
<PackageTags>Web Data Push Real-time Notifications ESB Message Broadcasting Distributed Computing</PackageTags>
<!--<Summary>PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously</Summary>-->
<Description>PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously</Description>
Expand Down
4 changes: 2 additions & 2 deletions src/Api/PubnubApiUWP/PubnubApiUWP.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@

<PropertyGroup>
<PackageId>PubnubUWP</PackageId>
<PackageVersion>8.0.3</PackageVersion>
<PackageVersion>8.0.4</PackageVersion>
<Title>PubNub C# .NET - Web Data Push API</Title>
<Authors>Pandu Masabathula</Authors>
<Owners>PubNub</Owners>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageIconUrl>http://pubnub.s3.amazonaws.com/2011/powered-by-pubnub/pubnub-icon-600x600.png</PackageIconUrl>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RepositoryUrl>https://github.com/pubnub/c-sharp/</RepositoryUrl>
<PackageReleaseNotes>Refactor EmitStatus to prevent concurrent modification issue resulting InvalidOperationException.</PackageReleaseNotes>
<PackageReleaseNotes>Prevent resubscribe to previously subscribed entities.</PackageReleaseNotes>
<PackageTags>Web Data Push Real-time Notifications ESB Message Broadcasting Distributed Computing</PackageTags>
<!--<Summary>PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously</Summary>-->
<Description>PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously</Description>
Expand Down
2 changes: 1 addition & 1 deletion src/Api/PubnubApiUnity/PubnubApiUnity.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<PropertyGroup>
<PackageId>PubnubApiUnity</PackageId>
<PackageVersion>8.0.3</PackageVersion>
<PackageVersion>8.0.4</PackageVersion>
<Title>PubNub C# .NET - Web Data Push API</Title>
<Authors>Pandu Masabathula</Authors>
<Owners>PubNub</Owners>
Expand Down
32 changes: 18 additions & 14 deletions src/UnitTests/PubnubApi.Tests/WhenAMessageIsPublished.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1780,21 +1780,22 @@ public static void IfMobilePayloadThenPublishReturnSuccess()
{
collapseId = "sample collapse id",
expiration = "xyzexpiration",
authMethod = APNS2AuthMethod.TOKEN,
targets = new List<PushTarget>()
{
new PushTarget()
{
environment= PubnubApi.Environment.Development,
exclude_devices = new List<string>(){ "excl_d1", "excl_d2" },
topic = "sample dev topic"
},
new PushTarget()
{
environment= PubnubApi.Environment.Production,
exclude_devices = new List<string>(){ "excl_d3", "excl_d4" },
topic = "sample prod topic"
}
}
{
new PushTarget()
{
environment= PubnubApi.Environment.Development,
exclude_devices = new List<string>(){ "excl_d1", "excl_d2" },
topic = "sample dev topic"
},
new PushTarget()
{
environment= PubnubApi.Environment.Production,
exclude_devices = new List<string>(){ "excl_d3", "excl_d4" },
topic = "sample prod topic"
}
}
};

Dictionary<PNPushType, Dictionary<string, object>> pushTypeCustomData = new Dictionary<PNPushType, Dictionary<string, object>>();
Expand Down Expand Up @@ -1824,6 +1825,9 @@ public static void IfMobilePayloadThenPublishReturnSuccess()
System.Diagnostics.Debug.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(payload));

Assert.IsTrue(payload != null, "FAILED - IfMobilePayloadThenPublishReturnSuccess");
Assert.True(payload.ContainsKey("pn_apns"), "FAILED - Push Payload should contain apns key");
Assert.True(payload.ContainsKey("pn_fcm"), "FAILED - Push Payload should contain fcm key");
Assert.False(payload.ContainsKey("pn_gcm"), "FAILED - Push Payload should NOT contain gcm key");
}

[Test]
Expand Down
Loading
Loading