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.2.1"
version: "8.3.0"
schema: 1
scm: github.com/pubnub/c-sharp
changelog:
- date: 2026-06-08
version: v8.3.0
changes:
- type: feature
text: "Added a EnableHttp2 flag to the config which if set to true (the default value) will make all calls in the HttpClientService request Http/2 by default and fall back to Http/1.1 if not available on the origin."
- date: 2026-05-29
version: v8.2.1
changes:
Expand Down Expand Up @@ -989,14 +994,14 @@ features:
- QUERY-PARAM
supported-platforms:
-
version: Pubnub 'C#' 8.2.1
version: Pubnub 'C#' 8.3.0
platforms:
- Windows 10 and up
- Windows Server 2008 and up
frameworks:
- .Net Framework 4.5+
-
version: PubnubPCL 'C#' 8.2.1
version: PubnubPCL 'C#' 8.3.0
platforms:
- Xamarin.Android
- Xamarin.iOS
Expand All @@ -1008,7 +1013,7 @@ supported-platforms:
frameworks:
- .Net 4.5+
-
version: PubnubUWP 'C#' 8.2.1
version: PubnubUWP 'C#' 8.3.0
platforms:
- Windows Phone 10
- Universal Windows Apps
Expand All @@ -1032,7 +1037,7 @@ sdks:
distribution-type: source
distribution-repository: GitHub
package-name: Pubnub
location: https://github.com/pubnub/c-sharp/releases/tag/v8.2.1
location: https://github.com/pubnub/c-sharp/releases/tag/v8.3.0
requires:
-
name: ".Net"
Expand Down Expand Up @@ -1273,7 +1278,7 @@ sdks:
distribution-type: source
distribution-repository: GitHub
package-name: PubNubPCL
location: https://github.com/pubnub/c-sharp/releases/tag/v8.2.1
location: https://github.com/pubnub/c-sharp/releases/tag/v8.3.0
requires:
-
name: ".Net"
Expand Down Expand Up @@ -1624,7 +1629,7 @@ sdks:
distribution-type: source
distribution-repository: GitHub
package-name: PubnubUWP
location: https://github.com/pubnub/c-sharp/releases/tag/v8.2.1
location: https://github.com/pubnub/c-sharp/releases/tag/v8.3.0
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.3.0 - June 08 2026
-----------------------------
- Added: added a EnableHttp2 flag to the config which if set to true (the default value) will make all calls in the HttpClientService request Http/2 by default and fall back to Http/1.1 if not available on the origin.

v8.2.1 - May 29 2026
-----------------------------
- Fixed: fixes issue of hereNow returning incorrect Occupancy when Occupancy count value is omitted from the response of single channel hereNow call.
Expand Down
13 changes: 13 additions & 0 deletions src/Api/PubnubApi/PNConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,18 @@ public PNReconnectionPolicy ReconnectionPolicy {

public int FileMessagePublishRetryLimit { get; set; }

/// <summary>
/// When enabled (default), each outbound request negotiates HTTP/2 using
/// <see cref="System.Net.Http.HttpRequestMessage.Version"/> / VersionPolicy, with
/// automatic fallback to HTTP/1.1. HTTP/2 only takes effect against an HTTP/2-capable
/// (customer-enabled) PubNub origin; otherwise requests transparently use HTTP/1.1.
/// The negotiated protocol is re-evaluated on new connections (natural reconnect or
/// Pubnub client recreation); the SDK does not pin or proactively probe for HTTP/2.
/// On target frameworks without VersionPolicy support (netstandard2.0/.NET Framework),
/// only the request version is set as a best-effort hint.
/// </summary>
public bool EnableHttp2 { get; set; } = true;

[Obsolete("PNConfiguration(string uuid) is deprecated, please use PNConfiguration(UserId userId) instead.")]
public PNConfiguration(string uuid)
{
Expand Down Expand Up @@ -286,6 +298,7 @@ private void ConstructorInit(UserId currentUserId)
userId = currentUserId;
LogLevel = PubnubLogLevel.None;
EnableEventEngine = true;
EnableHttp2 = true;
}

private void setDefaultRetryConfigurationFromPolicy(PNReconnectionPolicy policy)
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.2.1")]
[assembly: AssemblyFileVersion("8.2.1")]
[assembly: AssemblyVersion("8.3.0")]
[assembly: AssemblyFileVersion("8.3.0")]
// 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
2 changes: 1 addition & 1 deletion src/Api/PubnubApi/Pubnub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,7 @@ public Pubnub(PNConfiguration config, IHttpClientService httpTransportService =
//Defaulting to DotNet PNSDK source if no custom one is specified
Version = (ipnsdkSource == default) ? new DotNetPNSDKSource().GetPNSDK() : ipnsdkSource.GetPNSDK();
IHttpClientService httpClientService =
httpTransportService ?? new HttpClientService(proxy: config.Proxy);
httpTransportService ?? new HttpClientService(proxy: config.Proxy, enableHttp2: config.EnableHttp2);
httpClientService.SetLogger(logger);
transportMiddleware = middleware ?? new Middleware(httpClientService, config, this, tokenManager);
logger?.Debug(GetConfigurationLogString(config));
Expand Down
7 changes: 3 additions & 4 deletions src/Api/PubnubApi/PubnubApi.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net60</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>pubnub.snk</AssemblyOriginatorKeyFile>
Expand All @@ -14,16 +14,15 @@

<PropertyGroup>
<PackageId>Pubnub</PackageId>
<PackageVersion>8.2.1</PackageVersion>
<PackageVersion>8.3.0</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>Fixes issue of hereNow returning incorrect Occupancy when Occupancy count value is omitted from the response of single channel hereNow call.
Fix for `HereNow` omitting the channel entry for an empty channel when UUIDs are included.</PackageReleaseNotes>
<PackageReleaseNotes>Added a EnableHttp2 flag to the config which if set to true (the default value) will make all calls in the HttpClientService request Http/2 by default and fall back to Http/1.1 if not available on the origin.</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
71 changes: 65 additions & 6 deletions src/Api/PubnubApi/Transport/HttpClientService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ namespace PubnubApi
public class HttpClientService : IHttpClientService
{
private readonly HttpClient httpClient;
private readonly bool enableHttp2;
private PubnubLogModule logger;

public HttpClientService(IWebProxy proxy)
public HttpClientService(IWebProxy proxy) : this(proxy, true)
{
}

public HttpClientService(IWebProxy proxy, bool enableHttp2)
{
this.enableHttp2 = enableHttp2;
httpClient = new HttpClient()
{
Timeout = Timeout.InfiniteTimeSpan
Expand All @@ -28,6 +34,39 @@ public HttpClientService(IWebProxy proxy)
httpClient.Timeout = Timeout.InfiniteTimeSpan;
}

/// <summary>
/// Creates a transport backed by a caller-supplied <see cref="HttpMessageHandler"/>.
/// Intended for testing and advanced transport scenarios (e.g. observing outgoing
/// requests or stubbing responses). The supplied handler is fully responsible for
/// TLS/certificate validation and proxy behavior; the SDK does not weaken or override
/// them here. Do not pass a handler that disables certificate validation in production.
/// </summary>
/// <param name="handler">The message handler used to send requests.</param>
/// <param name="enableHttp2">Whether outbound requests should request HTTP/2 with HTTP/1.1 fallback.</param>
public HttpClientService(HttpMessageHandler handler, bool enableHttp2)
{
this.enableHttp2 = enableHttp2;
httpClient = new HttpClient(handler)
{
Timeout = Timeout.InfiniteTimeSpan
};
}

// HTTP/2 is requested by default; RequestVersionOrLower guarantees HTTP/1.1 fallback
// against non-HTTP/2 origins. Set per HttpRequestMessage because the SDK uses explicit
// request messages over a shared HttpClient.
private void ConfigureHttpVersion(HttpRequestMessage requestMessage)
{
if (!enableHttp2)
{
return;
}
requestMessage.Version = new Version(2, 0);
#if NET6_0_OR_GREATER || NET60
requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
#endif
}

public void SetLogger(PubnubLogModule logger)
{
this.logger = logger;
Expand All @@ -41,6 +80,7 @@ public async Task<TransportResponse> GetRequest(TransportRequest transportReques
{
HttpRequestMessage requestMessage =
new HttpRequestMessage(method: HttpMethod.Get, requestUri: transportRequest.RequestUrl);
ConfigureHttpVersion(requestMessage);
if (transportRequest.Headers.Keys.Count > 0)
{
foreach (var kvp in transportRequest.Headers)
Expand Down Expand Up @@ -68,8 +108,11 @@ public async Task<TransportResponse> GetRequest(TransportRequest transportReques
StatusCode = (int)httpResult.StatusCode,
Content = responseContent,
Headers = httpResult.Headers.ToDictionary(h => h.Key, h => h.Value),
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath,
NegotiatedProtocolVersion = httpResult.Version
};
logger?.Debug(
$"PubNub request completed: operation={transportRequest.OperationType} protocol=HTTP/{httpResult.Version}");
logger?.Debug(
$"HttpClient Service: Received http response from server with status code {httpResult.StatusCode}, content-length: {transportResponse.Content.Length} bytes, for url \n{transportRequest.RequestUrl}");
}
Expand Down Expand Up @@ -118,6 +161,7 @@ public async Task<TransportResponse> PostRequest(TransportRequest transportReque
HttpRequestMessage requestMessage =
new HttpRequestMessage(method: HttpMethod.Post, requestUri: transportRequest.RequestUrl)
{ Content = postData };
ConfigureHttpVersion(requestMessage);
// Set Http Request header, When the header is not a payload content header.
if (transportRequest.Headers.Keys.Count > 0 && transportRequest.BodyContentBytes == null)
{
Expand Down Expand Up @@ -145,8 +189,11 @@ public async Task<TransportResponse> PostRequest(TransportRequest transportReque
StatusCode = (int)httpResult.StatusCode,
Content = responseContent,
Headers = httpResult.Headers.ToDictionary(h => h.Key, h => h.Value),
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath,
NegotiatedProtocolVersion = httpResult.Version
};
logger?.Debug(
$"PubNub request completed: operation={transportRequest.OperationType} protocol=HTTP/{httpResult.Version}");
logger?.Debug(
$"Received http response from server with status code {httpResult.StatusCode}, content-length: {transportResponse.Content.Length} bytes, for url {transportRequest.RequestUrl}");
}
Expand Down Expand Up @@ -197,6 +244,7 @@ public async Task<TransportResponse> PutRequest(TransportRequest transportReques
HttpRequestMessage requestMessage =
new HttpRequestMessage(method: HttpMethod.Put, requestUri: transportRequest.RequestUrl)
{ Content = putData };
ConfigureHttpVersion(requestMessage);
if (transportRequest.Headers.Keys.Count > 0)
{
foreach (var kvp in transportRequest.Headers)
Expand Down Expand Up @@ -224,8 +272,11 @@ public async Task<TransportResponse> PutRequest(TransportRequest transportReques
StatusCode = (int)httpResult.StatusCode,
Content = responseContent,
Headers = httpResult.Headers.ToDictionary(h => h.Key, h => h.Value),
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath,
NegotiatedProtocolVersion = httpResult.Version
};
logger?.Debug(
$"PubNub request completed: operation={transportRequest.OperationType} protocol=HTTP/{httpResult.Version}");
logger?.Debug(
$"Received http response from server with status code {httpResult.StatusCode}, content-length: {transportResponse.Content.Length} bytes, for url {transportRequest.RequestUrl}");
}
Expand Down Expand Up @@ -260,6 +311,7 @@ public async Task<TransportResponse> DeleteRequest(TransportRequest transportReq
{
HttpRequestMessage requestMessage =
new HttpRequestMessage(method: HttpMethod.Delete, requestUri: transportRequest.RequestUrl);
ConfigureHttpVersion(requestMessage);
if (transportRequest.Headers.Keys.Count > 0)
{
foreach (var kvp in transportRequest.Headers)
Expand Down Expand Up @@ -287,8 +339,11 @@ public async Task<TransportResponse> DeleteRequest(TransportRequest transportReq
StatusCode = (int)httpResult.StatusCode,
Content = responseContent,
Headers = httpResult.Headers.ToDictionary(h => h.Key, h => h.Value),
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath,
NegotiatedProtocolVersion = httpResult.Version
};
logger?.Debug(
$"PubNub request completed: operation={transportRequest.OperationType} protocol=HTTP/{httpResult.Version}");
logger?.Debug(
$"Received http response from server with status code {httpResult.StatusCode}, content-length: {transportResponse.Content.Length} bytes, for url {transportRequest.RequestUrl}");
}
Expand Down Expand Up @@ -340,6 +395,7 @@ public async Task<TransportResponse> PatchRequest(TransportRequest transportRequ
HttpRequestMessage requestMessage =
new HttpRequestMessage(new HttpMethod("PATCH"), requestUri: transportRequest.RequestUrl)
{ Content = patchData };
ConfigureHttpVersion(requestMessage);
if (transportRequest.Headers.Keys.Count > 0)
{
foreach (var kvp in transportRequest.Headers)
Expand Down Expand Up @@ -367,8 +423,11 @@ public async Task<TransportResponse> PatchRequest(TransportRequest transportRequ
StatusCode = (int)httpResult.StatusCode,
Content = responseContent,
Headers = httpResult.Headers.ToDictionary(h => h.Key, h => h.Value),
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath
RequestUrl = httpResult.RequestMessage?.RequestUri?.AbsolutePath,
NegotiatedProtocolVersion = httpResult.Version
};
logger?.Debug(
$"PubNub request completed: operation={transportRequest.OperationType} protocol=HTTP/{httpResult.Version}");
logger?.Debug(
$"Received http response from server with status code {httpResult.StatusCode}, content-length: {transportResponse.Content.Length} bytes, for url {transportRequest.RequestUrl}");
}
Expand Down
1 change: 1 addition & 0 deletions src/Api/PubnubApi/Transport/Middleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public TransportRequest PreapareTransportRequest(RequestParameter requestParamet
var transportRequest = new TransportRequest()
{
RequestType = requestParameter.RequestType,
OperationType = operationType,
RequestUrl = urlString,
BodyContentString = requestParameter.BodyContentString,
FormData = requestParameter.FormData,
Expand Down
1 change: 1 addition & 0 deletions src/Api/PubnubApi/TransportContract/TransportRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace PubnubApi
public class TransportRequest
{
public string RequestType { get; set; }
public PNOperationType OperationType { get; set; }
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
public string RequestUrl { get; set; }
public byte[] FormData { get; set; } = default;
Expand Down
6 changes: 6 additions & 0 deletions src/Api/PubnubApi/TransportContract/TransportResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ public class TransportResponse
public byte[] Content { get; set; }
public Dictionary<string, IEnumerable<string>> Headers { get; set; }
public string RequestUrl { get; set; }

/// <summary>
/// The negotiated HTTP protocol version of the response (e.g. 2.0 or 1.1),
/// taken from HttpResponseMessage.Version. Null when no response was received.
/// </summary>
public Version NegotiatedProtocolVersion { get; set; }
public Exception Error { get; set; }

public bool IsTimeOut {get; set;}
Expand Down
5 changes: 2 additions & 3 deletions src/Api/PubnubApiPCL/PubnubApiPCL.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@

<PropertyGroup>
<PackageId>PubnubPCL</PackageId>
<PackageVersion>8.2.1</PackageVersion>
<PackageVersion>8.3.0</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>Fixes issue of hereNow returning incorrect Occupancy when Occupancy count value is omitted from the response of single channel hereNow call.
Fix for `HereNow` omitting the channel entry for an empty channel when UUIDs are included.</PackageReleaseNotes>
<PackageReleaseNotes>Added a EnableHttp2 flag to the config which if set to true (the default value) will make all calls in the HttpClientService request Http/2 by default and fall back to Http/1.1 if not available on the origin.</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
Loading
Loading