diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/connection.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/connection.js index 6bcf16776e..96f82709fd 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/connection.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/connection.js @@ -123,12 +123,27 @@ class Connection extends EventEmitter { headers[utils.getUserAgentHeader()] = await utils.getUserAgent(); } } - - const WebSocket = globalThis.WebSocket ?? (await import('ws')).default; + // All these options are available to the `ws` package's constructor, but not the global WebSocket class + const wsSpecificOptions = new Set([ + 'headers', + 'ca', + 'cert', + 'pfx', + 'rejectUnauthorized', + 'agent', + 'perMessageDeflate', + ]); + // Check if any `ws` specific options are provided and are non-null / non-undefined + const hasWsSpecificOptions = Object.entries(this.options).some( + ([key, value]) => wsSpecificOptions.has(key) && ![null, undefined].includes(value), + ); + // Only use the global websocket if we don't have any unsupported options + const useGlobalWebSocket = !hasWsSpecificOptions && globalThis.WebSocket; + const WebSocket = useGlobalWebSocket || (await import('ws')).default; this._ws = new WebSocket( this.url, - globalThis.WebSocket === undefined + !useGlobalWebSocket ? { headers: headers, ca: this.options.ca, diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/socket-connection-tests.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/socket-connection-tests.js index 43bb80aceb..47ac00fa91 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/socket-connection-tests.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/socket-connection-tests.js @@ -49,7 +49,45 @@ describe('Connection', function () { }); describe('#open()', function () { + it('should use the ws WebSocket when ws specific options are provided', function () { + let globalWebsocketCalls = 0; + globalThis.WebSocket = function () { + globalWebsocketCalls++; + }; + const wsSpecificOptions = [ + 'headers', + 'ca', + 'cert', + 'pfx', + 'rejectUnauthorized', + 'agent', + 'perMessageDeflate', + ]; + const allOptionTests = wsSpecificOptions.map((wsOption) => { + const connection = helper.getDriverRemoteConnection(`ws://localhost:${testServerPort}/401`, { + [wsOption]: 'this option is set', + }); + return connection.open(); + }); + return Promise.allSettled(allOptionTests).then(function () { + assert.equal(globalWebsocketCalls, 0, 'global WebSocket should be used when no ws specific options are provided'); + }); + }); + it('should use the global WebSocket when options are not provided', function () { + let globalWebsocketCalls = 0; + globalThis.WebSocket = function () { + globalWebsocketCalls++; + }; + const connection = helper.getDriverRemoteConnection(`ws://localhost:${testServerPort}/401`); + return connection + .open() + .catch(() => {}) + .finally(function () { + assert.equal(globalWebsocketCalls, 1, 'global WebSocket should be used when no ws specific options are provided'); + }); + }); it('should handle unexpected response errors with body', function () { + globalThis.WebSocket = http.WebSocket; const connection = helper.getDriverRemoteConnection(`ws://localhost:${testServerPort}/401`); return connection .open() @@ -63,6 +101,7 @@ describe('Connection', function () { }); }); it('should handle unexpected response errors with no body', function () { + globalThis.WebSocket = undefined; const connection = helper.getDriverRemoteConnection(`ws://localhost:${testServerPort}/404`); return connection .open()