Skip to content

Commit

Permalink
Fix presence leave call on used channels (#425)
Browse files Browse the repository at this point in the history
fix(presence): fix presence leave call on used channels

Fix issue with `Subscription` and `SubscriptionSet` when one can unsubscribe channel / group which is still in use by another.

fix(network): fix fetch resource error report as bad request

Fix particular `TypeError` emitted when browser forcefully closes long-poll connection before its timeout and reported as bad request. This type of error will be reported as a network error.
  • Loading branch information
parfeon authored Dec 12, 2024
1 parent f51f759 commit d9af3ec
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 28 deletions.
15 changes: 12 additions & 3 deletions .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
---
changelog:
- date: 2024-12-12
version: v8.3.2
changes:
- type: bug
text: "Fix issue with `Subscription` and `SubscriptionSet` when one can unsubscribe channel / group which is still in use by another."
- type: bug
text: "Fix particular `TypeError` emitted when browser forcefully closes long-poll connection before its timeout and reported as bad request. This type of error will be reported as a network error."
- type: bug
text: "Fix issue because of which `node-fetch` used default agent, which after Node.js 19+ has `keepAlive` enabled by default."
- date: 2024-11-18
version: v8.3.1
changes:
Expand Down Expand Up @@ -1072,7 +1081,7 @@ supported-platforms:
- 'Ubuntu 14.04 and up'
- 'Windows 7 and up'
version: 'Pubnub Javascript for Node'
version: '8.3.1'
version: '8.3.2'
sdks:
- full-name: PubNub Javascript SDK
short-name: Javascript
Expand All @@ -1088,7 +1097,7 @@ sdks:
- distribution-type: source
distribution-repository: GitHub release
package-name: pubnub.js
location: https://github.com/pubnub/javascript/archive/refs/tags/v8.3.1.zip
location: https://github.com/pubnub/javascript/archive/refs/tags/v8.3.2.zip
requires:
- name: 'agentkeepalive'
min-version: '3.5.2'
Expand Down Expand Up @@ -1759,7 +1768,7 @@ sdks:
- distribution-type: library
distribution-repository: GitHub release
package-name: pubnub.js
location: https://github.com/pubnub/javascript/releases/download/v8.3.1/pubnub.8.3.1.js
location: https://github.com/pubnub/javascript/releases/download/v8.3.2/pubnub.8.3.2.js
requires:
- name: 'agentkeepalive'
min-version: '3.5.2'
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## v8.3.2
December 12 2024

#### Fixed
- Fix issue with `Subscription` and `SubscriptionSet` when one can unsubscribe channel / group which is still in use by another.
- Fix particular `TypeError` emitted when browser forcefully closes long-poll connection before its timeout and reported as bad request. This type of error will be reported as a network error.
- Fix issue because of which `node-fetch` used default agent, which after Node.js 19+ has `keepAlive` enabled by default.

## v8.3.1
November 18 2024

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ Watch [Getting Started with PubNub JS SDK](https://app.dashcam.io/replay/64ee0d2
npm install pubnub
```
* or download one of our builds from our CDN:
* https://cdn.pubnub.com/sdk/javascript/pubnub.8.3.1.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.8.3.1.min.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.8.3.2.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.8.3.2.min.js
2. Configure your keys:
Expand Down
71 changes: 67 additions & 4 deletions dist/web/pubnub.js
Original file line number Diff line number Diff line change
Expand Up @@ -2916,7 +2916,10 @@
message = 'Network issues';
}
else if (errorName === 'TypeError') {
category = StatusCategory$1.PNBadRequestCategory;
if (message.indexOf('Load failed') !== -1 || message.indexOf('Failed to fetch') != -1)
category = StatusCategory$1.PNNetworkIssuesCategory;
else
category = StatusCategory$1.PNBadRequestCategory;
}
else if (errorName === 'FetchError') {
const errorCode = error.code;
Expand Down Expand Up @@ -3948,7 +3951,7 @@
return base.PubNubFile;
},
get version() {
return '8.3.1';
return '8.3.2';
},
getVersion() {
return this.version;
Expand Down Expand Up @@ -9892,15 +9895,23 @@
*/
subscribe(subscribeParameters) {
const timetoken = subscribeParameters === null || subscribeParameters === void 0 ? void 0 : subscribeParameters.timetoken;
this.pubnub.registerSubscribeCapable(this);
this.pubnub.subscribe(Object.assign({ channels: this.channelNames, channelGroups: this.groupNames }, (timetoken !== null && timetoken !== '' && { timetoken: timetoken })));
}
/**
* Stop real-time events processing.
*/
unsubscribe() {
this.pubnub.unregisterSubscribeCapable(this);
const { channels, channelGroups } = this.pubnub.getSubscribeCapableEntities();
// Identify channels and groups from which PubNub client can safely unsubscribe.
const filteredChannelGroups = this.groupNames.filter((cg) => !channelGroups.includes(cg));
const filteredChannels = this.channelNames.filter((ch) => !channels.includes(ch));
if (filteredChannels.length === 0 && filteredChannelGroups.length === 0)
return;
this.pubnub.unsubscribe({
channels: this.channelNames,
channelGroups: this.groupNames,
channels: filteredChannels,
channelGroups: filteredChannelGroups,
});
}
/**
Expand Down Expand Up @@ -12566,6 +12577,7 @@
// Prepare for real-time events announcement.
this.listenerManager = new ListenerManager();
this.eventEmitter = new EventEmitter(this.listenerManager);
this.subscribeCapable = new Set();
if (this._configuration.enableEventEngine) {
{
let heartbeatInterval = this._configuration.getHeartbeatInterval();
Expand Down Expand Up @@ -12998,7 +13010,9 @@
* @param [isOffline] - Whether `offline` presence should be notified or not.
*/
destroy(isOffline) {
var _a;
{
(_a = this.subscribeCapable) === null || _a === void 0 ? void 0 : _a.clear();
if (this.subscriptionManager) {
this.subscriptionManager.unsubscribeAll(isOffline);
this.subscriptionManager.disconnect();
Expand Down Expand Up @@ -13127,6 +13141,53 @@
}
return [];
}
/**
* Register subscribe capable object with active subscription.
*
* @param subscribeCapable - {@link Subscription} or {@link SubscriptionSet} object.
*
* @internal
*/
registerSubscribeCapable(subscribeCapable) {
{
if (!this.subscribeCapable || this.subscribeCapable.has(subscribeCapable))
return;
this.subscribeCapable.add(subscribeCapable);
}
}
/**
* Unregister subscribe capable object with inactive subscription.
*
* @param subscribeCapable - {@link Subscription} or {@link SubscriptionSet} object.
*
* @internal
*/
unregisterSubscribeCapable(subscribeCapable) {
{
if (!this.subscribeCapable || !this.subscribeCapable.has(subscribeCapable))
return;
this.subscribeCapable.delete(subscribeCapable);
}
}
/**
* Retrieve list of subscribe capable entities currently used in subscription.
*
* @returns Channels and channel groups currently used in subscription.
*
* @internal
*/
getSubscribeCapableEntities() {
{
const entities = { channels: [], channelGroups: [] };
if (!this.subscribeCapable)
return entities;
for (const subscribeCapable of this.subscribeCapable) {
entities.channelGroups.push(...subscribeCapable.channelGroups);
entities.channels.push(...subscribeCapable.channels);
}
return entities;
}
}
/**
* Subscribe to specified channels and groups real-time events.
*
Expand Down Expand Up @@ -13205,7 +13266,9 @@
* Unsubscribe from all channels and groups.
*/
unsubscribeAll() {
var _a;
{
(_a = this.subscribeCapable) === null || _a === void 0 ? void 0 : _a.clear();
if (this.subscriptionManager)
this.subscriptionManager.unsubscribeAll();
else if (this.eventEngine)
Expand Down
4 changes: 2 additions & 2 deletions dist/web/pubnub.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/core/components/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const makeConfiguration = (base, setupCryptoModule) => {
return base.PubNubFile;
},
get version() {
return '8.3.1';
return '8.3.2';
},
getVersion() {
return this.version;
Expand Down
60 changes: 60 additions & 0 deletions lib/core/pubnub-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ class PubNubCore {
// Prepare for real-time events announcement.
this.listenerManager = new listener_manager_1.ListenerManager();
this.eventEmitter = new eventEmitter_1.default(this.listenerManager);
this.subscribeCapable = new Set();
if (this._configuration.enableEventEngine) {
if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled') {
let heartbeatInterval = this._configuration.getHeartbeatInterval();
Expand Down Expand Up @@ -609,7 +610,10 @@ class PubNubCore {
* @param [isOffline] - Whether `offline` presence should be notified or not.
*/
destroy(isOffline) {
var _a;
if (process.env.SUBSCRIBE_MODULE !== 'disabled') {
if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled')
(_a = this.subscribeCapable) === null || _a === void 0 ? void 0 : _a.clear();
if (this.subscriptionManager) {
this.subscriptionManager.unsubscribeAll(isOffline);
this.subscriptionManager.disconnect();
Expand Down Expand Up @@ -755,6 +759,59 @@ class PubNubCore {
throw new Error('Subscription error: subscription module disabled');
return [];
}
/**
* Register subscribe capable object with active subscription.
*
* @param subscribeCapable - {@link Subscription} or {@link SubscriptionSet} object.
*
* @internal
*/
registerSubscribeCapable(subscribeCapable) {
if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled') {
if (!this.subscribeCapable || this.subscribeCapable.has(subscribeCapable))
return;
this.subscribeCapable.add(subscribeCapable);
}
else
throw new Error('Subscription error: subscription event engine module disabled');
}
/**
* Unregister subscribe capable object with inactive subscription.
*
* @param subscribeCapable - {@link Subscription} or {@link SubscriptionSet} object.
*
* @internal
*/
unregisterSubscribeCapable(subscribeCapable) {
if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled') {
if (!this.subscribeCapable || !this.subscribeCapable.has(subscribeCapable))
return;
this.subscribeCapable.delete(subscribeCapable);
}
else
throw new Error('Subscription error: subscription event engine module disabled');
}
/**
* Retrieve list of subscribe capable entities currently used in subscription.
*
* @returns Channels and channel groups currently used in subscription.
*
* @internal
*/
getSubscribeCapableEntities() {
if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled') {
const entities = { channels: [], channelGroups: [] };
if (!this.subscribeCapable)
return entities;
for (const subscribeCapable of this.subscribeCapable) {
entities.channelGroups.push(...subscribeCapable.channelGroups);
entities.channels.push(...subscribeCapable.channels);
}
return entities;
}
else
throw new Error('Subscription error: subscription event engine module disabled');
}
/**
* Subscribe to specified channels and groups real-time events.
*
Expand Down Expand Up @@ -841,7 +898,10 @@ class PubNubCore {
* Unsubscribe from all channels and groups.
*/
unsubscribeAll() {
var _a;
if (process.env.SUBSCRIBE_MODULE !== 'disabled') {
if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled')
(_a = this.subscribeCapable) === null || _a === void 0 ? void 0 : _a.clear();
if (this.subscriptionManager)
this.subscriptionManager.unsubscribeAll();
else if (this.eventEngine)
Expand Down
12 changes: 10 additions & 2 deletions lib/entities/SubscribeCapable.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ class SubscribeCapable {
*/
subscribe(subscribeParameters) {
const timetoken = subscribeParameters === null || subscribeParameters === void 0 ? void 0 : subscribeParameters.timetoken;
this.pubnub.registerSubscribeCapable(this);
this.pubnub.subscribe(Object.assign({ channels: this.channelNames, channelGroups: this.groupNames }, (timetoken !== null && timetoken !== '' && { timetoken: timetoken })));
}
/**
* Stop real-time events processing.
*/
unsubscribe() {
this.pubnub.unregisterSubscribeCapable(this);
const { channels, channelGroups } = this.pubnub.getSubscribeCapableEntities();
// Identify channels and groups from which PubNub client can safely unsubscribe.
const filteredChannelGroups = this.groupNames.filter((cg) => !channelGroups.includes(cg));
const filteredChannels = this.channelNames.filter((ch) => !channels.includes(ch));
if (filteredChannels.length === 0 && filteredChannelGroups.length === 0)
return;
this.pubnub.unsubscribe({
channels: this.channelNames,
channelGroups: this.groupNames,
channels: filteredChannels,
channelGroups: filteredChannelGroups,
});
}
/**
Expand Down
5 changes: 4 additions & 1 deletion lib/errors/pubnub-api-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ class PubNubAPIError extends Error {
message = 'Network issues';
}
else if (errorName === 'TypeError') {
category = categories_1.default.PNBadRequestCategory;
if (message.indexOf('Load failed') !== -1 || message.indexOf('Failed to fetch') != -1)
category = categories_1.default.PNNetworkIssuesCategory;
else
category = categories_1.default.PNBadRequestCategory;
}
else if (errorName === 'FetchError') {
const errorCode = error.code;
Expand Down
11 changes: 4 additions & 7 deletions lib/transport/node-transport.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,16 @@ class NodeTransport {
* @internal
*/
agentForTransportRequest(req) {
// Don't configure any agents if keep alive not requested.
if (!this.keepAlive && !this.proxyConfiguration)
return undefined;
// Create proxy agent (if possible).
if (this.proxyConfiguration)
return this.proxyAgent ? this.proxyAgent : (this.proxyAgent = new proxy_agent_1.ProxyAgent(this.proxyConfiguration));
// Create keep alive agent.
const useSecureAgent = req.origin.startsWith('https:');
const agentOptions = Object.assign({ keepAlive: this.keepAlive }, (this.keepAlive ? this.keepAliveSettings : {}));
if (useSecureAgent && this.httpsAgent === undefined)
this.httpsAgent = new https_1.Agent(Object.assign({ keepAlive: true }, this.keepAliveSettings));
else if (!useSecureAgent && this.httpAgent === undefined) {
this.httpAgent = new http_1.Agent(Object.assign({ keepAlive: true }, this.keepAliveSettings));
}
this.httpsAgent = new https_1.Agent(agentOptions);
else if (!useSecureAgent && this.httpAgent === undefined)
this.httpAgent = new http_1.Agent(agentOptions);
return useSecureAgent ? this.httpsAgent : this.httpAgent;
}
/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pubnub",
"version": "8.3.1",
"version": "8.3.2",
"author": "PubNub <support@pubnub.com>",
"description": "Publish & Subscribe Real-time Messaging with PubNub",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/core/components/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export const makeConfiguration = (
return base.PubNubFile;
},
get version(): string {
return '8.3.1';
return '8.3.2';
},
getVersion(): string {
return this.version;
Expand Down
Loading

0 comments on commit d9af3ec

Please sign in to comment.