diff --git a/addons/bus/static/src/services/bus_service.js b/addons/bus/static/src/services/bus_service.js index 537e599587f..330949f6a4e 100644 --- a/addons/bus/static/src/services/bus_service.js +++ b/addons/bus/static/src/services/bus_service.js @@ -6,6 +6,7 @@ import { registry } from '@web/core/registry'; import { session } from '@web/session'; import { isIosApp } from '@web/core/browser/feature_detection'; import { WORKER_VERSION } from "@bus/workers/websocket_worker"; +import legacySession from "web.session"; const { EventBus } = owl; @@ -29,8 +30,17 @@ export const busService = { multiTab.removeSharedValue('last_notification_id'); } const bus = new EventBus(); + let workerURL = `${legacySession.prefix}/bus/websocket_worker_bundle?v=${WORKER_VERSION}`; + if (legacySession.prefix !== window.origin) { + // Bus service is loaded from a different origin than the bundle + // URL. The Worker expects an URL from this origin, give it a base64 + // URL that will then load the bundle via "importScripts" which + // allows cross origin. + const source = `importScripts("${workerURL}");`; + workerURL = 'data:application/javascript;base64,' + window.btoa(source); + } const workerClass = 'SharedWorker' in window && !isIosApp() ? browser.SharedWorker : browser.Worker; - const worker = new workerClass(`/bus/websocket_worker_bundle?v=${WORKER_VERSION}`, { + const worker = new workerClass(workerURL, { name: 'SharedWorker' in window && !isIosApp() ? 'odoo:websocket_shared_worker' : 'odoo:websocket_worker', }); const connectionInitializedDeferred = new Deferred(); @@ -95,6 +105,7 @@ export const busService = { uid = false; } send('initialize_connection', { + websocketURL: `${legacySession.prefix.replace("http", "ws")}/websocket`, debug: odoo.debug, lastNotificationId: multiTab.getSharedValue('last_notification_id', 0), uid, diff --git a/addons/bus/static/src/workers/websocket_worker.js b/addons/bus/static/src/workers/websocket_worker.js index d19b0f99f88..a2a3437f545 100644 --- a/addons/bus/static/src/workers/websocket_worker.js +++ b/addons/bus/static/src/workers/websocket_worker.js @@ -34,7 +34,7 @@ export const WEBSOCKET_CLOSE_CODES = Object.freeze({ }); // Should be incremented on every worker update in order to force // update of the worker in browser cache. -export const WORKER_VERSION = "1.0.0"; +export const WORKER_VERSION = "1.0.1"; /** * This class regroups the logic necessary in order for the @@ -44,8 +44,8 @@ export const WORKER_VERSION = "1.0.0"; * for SharedWorker and this class implements it. */ export class WebsocketWorker { - constructor(websocketURL) { - this.websocketURL = websocketURL; + constructor() { + this.websocketURL = ""; this.currentUID = null; this.isWaitingForNewUID = true; this.channelsByClient = new Map(); @@ -200,12 +200,14 @@ export class WebsocketWorker { * given client. * @param {Number} [param0.lastNotificationId] Last notification id * known by the client. + * @param {String} [param0.websocketURL] URL of the websocket endpoint. * @param {Number|false|undefined} [param0.uid] Current user id * - Number: user is logged whether on the frontend/backend. * - false: user is not logged. * - undefined: not available (e.g. livechat support page) */ - _initializeConnection(client, { debug, lastNotificationId, uid }) { + _initializeConnection(client, { debug, lastNotificationId, uid, websocketURL }) { + this.websocketURL = websocketURL; this.lastNotificationId = lastNotificationId; this.debugModeByClient[client] = debug; this.isDebug = Object.values(this.debugModeByClient).some(debugValue => debugValue !== ''); diff --git a/addons/bus/static/src/workers/websocket_worker_script.js b/addons/bus/static/src/workers/websocket_worker_script.js index 58076b78972..be57f7fa087 100644 --- a/addons/bus/static/src/workers/websocket_worker_script.js +++ b/addons/bus/static/src/workers/websocket_worker_script.js @@ -5,9 +5,7 @@ import { WebsocketWorker } from "./websocket_worker"; (function () { - const websocketWorker = new WebsocketWorker( - `${self.location.protocol === 'https:' ? 'wss' : 'ws'}://${self.location.host}/websocket` - ); + const websocketWorker = new WebsocketWorker(); if (self.name.includes('shared')) { // The script is running in a shared worker: let's register every diff --git a/addons/bus/static/tests/bus_tests.js b/addons/bus/static/tests/bus_tests.js index 668d6bf5e72..7fd90282c83 100644 --- a/addons/bus/static/tests/bus_tests.js +++ b/addons/bus/static/tests/bus_tests.js @@ -13,6 +13,7 @@ const { registry } = require("@web/core/registry"); const { session } = require('@web/session'); const { makeDeferred, nextTick, patchWithCleanup } = require("@web/../tests/helpers/utils"); const { makeTestEnv } = require('@web/../tests/helpers/mock_env'); +const legacySession = require('web.session'); QUnit.module('Bus', { beforeEach: function () { @@ -461,6 +462,26 @@ QUnit.module('Bus', { 'connect', ]); }); + + QUnit.test("WebSocket connects with URL corresponding to session prefix", async function (assert) { + patchWebsocketWorkerWithCleanup(); + const origin = "http://random-website.com"; + patchWithCleanup(legacySession, { + prefix: origin, + }); + const websocketCreatedDeferred = makeDeferred(); + patchWithCleanup(window, { + WebSocket: function (url) { + assert.step(url); + websocketCreatedDeferred.resolve(); + return new EventTarget(); + }, + }, { pure: true }); + const env = await makeTestEnv(); + env.services["bus_service"].start(); + await websocketCreatedDeferred; + assert.verifySteps([`${origin.replace("http", "ws")}/websocket`]); + }); }); }); diff --git a/addons/bus/static/tests/helpers/mock_websocket.js b/addons/bus/static/tests/helpers/mock_websocket.js index 6cf13f92154..23730eed884 100644 --- a/addons/bus/static/tests/helpers/mock_websocket.js +++ b/addons/bus/static/tests/helpers/mock_websocket.js @@ -6,10 +6,9 @@ import { patchWithCleanup } from "@web/../tests/helpers/utils"; import { registerCleanup } from "@web/../tests/helpers/cleanup"; class WebSocketMock extends EventTarget { - constructor(url) { + constructor() { super(); this.readyState = 0; - this.url = url; queueMicrotask(() => { this.readyState = 1; @@ -71,7 +70,7 @@ export function patchWebsocketWorkerWithCleanup(params = {}) { }, }, { pure: true }); patchWithCleanup(websocketWorker || WebsocketWorker.prototype, params); - websocketWorker = websocketWorker || new WebsocketWorker('wss://odoo.com/websocket'); + websocketWorker = websocketWorker || new WebsocketWorker(); patchWithCleanup(browser, { SharedWorker: function () { const sharedWorker = new SharedWorkerMock(websocketWorker);