Skip to content

fix(data-channel): connect failure with multiple meetings#4941

Open
Tianhui-Han wants to merge 8 commits intowebex:nextfrom
Tianhui-Han:fix/PS_DataChannel_Connect_Failure_with_Multiple_Meetings
Open

fix(data-channel): connect failure with multiple meetings#4941
Tianhui-Han wants to merge 8 commits intowebex:nextfrom
Tianhui-Han:fix/PS_DataChannel_Connect_Failure_with_Multiple_Meetings

Conversation

@Tianhui-Han
Copy link
Copy Markdown
Contributor

@Tianhui-Han Tianhui-Han commented Apr 30, 2026

This pull request addresses

https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-809708

by making the following changes

When multiple meetings coexist, the shared refresh handler gets overwritten and PS token refresh targets the wrong meeting (401/403).

Fix: interceptor now resolves the owning meeting from the request URL at refresh time, falling back to the singleton handler if unresolved.

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Tooling change
  • Internal code refactor

The following scenarios were tested

< ENUMERATE TESTS PERFORMED, WHETHER MANUAL OR AUTOMATED >

The GAI Coding Policy And Copyright Annotation Best Practices

  • GAI was not used (or, no additional notation is required)
  • Code was generated entirely by GAI
  • GAI was used to create a draft that was subsequently customized or modified
  • Coder created a draft manually that was non-substantively modified by GAI (e.g., refactoring was performed by GAI on manually written code)
  • Tool used for AI assistance (GitHub Copilot / Other - specify)
    • Github Copilot
    • Other - Please Specify
  • This PR is related to
    • Feature
    • Defect fix
    • Tech Debt
    • Automation

I certified that

  • I have read and followed contributing guidelines
  • I discussed changes with code owners prior to submitting this pull request
  • I have not skipped any automated checks
  • All existing and new tests passed
  • I have updated the documentation accordingly

Make sure to have followed the contributing guidelines before submitting.

@Tianhui-Han Tianhui-Han marked this pull request as ready for review May 6, 2026 00:57
@Tianhui-Han Tianhui-Han requested review from a team as code owners May 6, 2026 00:57
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1d4e9958a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +60 to +62
typeof requestUrl === 'string' && requestUrl.includes(PRACTICE_SESSION_URL_MARKER)
? LLM_PRACTICE_SESSION
: LLM_DEFAULT_SESSION;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Detect practice session from encoded URL correctly

requestUrl.includes('practiceSession') misclassifies practice-session datachannel requests when the locus path is encoded (the normal URL shape is /datachannel/.../locus/<base64>/registrations, e.g. packages/@webex/plugin-meetings/test/unit/spec/fixture/locus.js), because the plaintext marker does not appear inside base64. In that case the interceptor always picks LLM_DEFAULT_SESSION, resolves the wrong meeting/token, and retries 401/403 with an invalid token for practice-session traffic.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c2f4e5e15

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +92 to +93
if (!result?.body) return undefined;
const {datachannelToken, dataChannelTokenType} = result.body;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Throw when token refresh returns no payload

When meeting.refreshDataChannelToken() (or internal.llm.refreshDataChannelToken()) fails, it can return null; this new guard converts that into undefined and the callers still overwrite Data-Channel-Auth-Token with that value. In the expired-token flow this strips the auth header, sends an unauthenticated request, and then onResponseError skips refresh entirely because !token is true, so recovery stops after a transient refresh failure. Previously this path threw during destructuring and preserved the existing header instead of clearing it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b559499f74

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +70 to +73
// If we couldn't identify the session from the URL, default to LLM_DEFAULT_SESSION
if (!sessionId) {
sessionId = LLM_DEFAULT_SESSION;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid default-session fallback on unmatched request URLs

When URL matching fails, this code unconditionally routes refresh to LLM_DEFAULT_SESSION, which can refresh the wrong meeting/token for practice-session traffic. This is reachable because request URLs are mutated earlier by host mapping (packages/@webex/webex-core/src/lib/interceptors/hostmap.js, onRequest rewrites options.uri), while getDatachannelUrl() stores the original session URL; after rewrite, startsWith() can miss and sessionId falls back to default. In multi-session webinars, that misroute returns a default-session token for a practice-session request and causes repeated 401/403 retries instead of recovery.

Useful? React with 👍 / 👎.

@Tianhui-Han Tianhui-Han changed the title fix(PS_Datachannel): connect failure with multiple meetings fix(data-channel): connect failure with multiple meetings May 6, 2026
export const LOCUS_URL = 'locusUrl';
export const END = 'end';
export const LLM_PRACTICE_SESSION = 'llm-practice-session';
export const LLM_DEFAULT_SESSION = 'llm-default-session';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have LLM_DEFAULT_SESSION defined here: https://github.com/webex/webex-js-sdk/blob/next/packages/%40webex/internal-plugin-llm/src/constants.ts#L4
it would be better to use that instead of creating a copy

result = await meeting.refreshDataChannelToken();
} else {
// @ts-ignore
result = await this.internal.llm.refreshDataChannelToken();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking at this code makes me realize that existing LLM module APIs are a bit flawed.... LLM plugin's refreshDataChannelToken() and setDatachannelToken() methods should take session ids as arguments, shouldn't they?


if (typeof requestUrl === 'string') {
// @ts-ignore
const psDatachannelUrl = this.internal.llm.getDatachannelUrl?.(LLM_PRACTICE_SESSION);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code will stop working when any meeting object creates an LLM session with a non-default session id or if we ever have more than 1 active LLM connection. Wouldn't it be better to get the list of all LLM sessions and among them look for the locus url? or maybe even don't bother with checking the llm sessions and just look up the meeting by locus url and call meeting.refreshDataChannelToken() on it?

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9a34e42cc3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +63 to +66
if (psDatachannelUrl && requestUrl.startsWith(psDatachannelUrl)) {
sessionId = LLM_PRACTICE_SESSION;
} else if (defaultDatachannelUrl && requestUrl.startsWith(defaultDatachannelUrl)) {
sessionId = LLM_DEFAULT_SESSION;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Match session URLs resiliently to hostmap rewrites

requestUrl.startsWith(ps/defaultDatachannelUrl) compares full absolute URLs, but HostMapInterceptor.onRequest() rewrites options.uri to a mapped host while internal.llm.getDatachannelUrl() stores the original registration URL from registerAndConnect(). In that common path the comparison fails, sessionId is left unset, and the new default-session fallback routes practice-session retries to the default meeting token, causing repeated 401/403 instead of recovery. This needs a host-agnostic match (or direct fallback to the singleton refresh handler when URL matching is inconclusive).

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants