Skip to content
8 changes: 7 additions & 1 deletion src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,14 @@ module.exports = class Client {
if (clientOptions.DEBUG) global.TW_DEBUG = clientOptions.DEBUG;

const server = clientOptions.server || 'data';
this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket?type=chart`, {
this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket?from=chart&type=chart`, {
origin: 'https://www.tradingview.com',
headers: {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept-Language': 'en-US,en;q=0.9',
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
},
});

if (clientOptions.token) {
Expand Down
8 changes: 6 additions & 2 deletions src/miscRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,11 @@ module.exports = {
* @param {string} [location] Auth page location (For france: https://fr.tradingview.com/)
* @returns {Promise<User>} Token
*/
async getUser(session, signature = '', location = 'https://www.tradingview.com/') {
async getUser(session, signature = '', location = 'https://www.tradingview.com/', redirectCount = 0) {
if (redirectCount > 5) {
throw new Error('Too many redirects - possible WAF or geo-restriction');
}

const { data, headers } = await axios.get(location, {
headers: {
cookie: genAuthCookies(session, signature),
Expand Down Expand Up @@ -461,7 +465,7 @@ module.exports = {
}

if (headers.location !== location) {
return this.getUser(session, signature, headers.location);
return this.getUser(session, signature, headers.location, redirectCount + 1);
}

throw new Error('Wrong or expired sessionid/signature');
Expand Down
49 changes: 49 additions & 0 deletions tests/getUser-redirect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, it, expect } from 'vitest';

describe('getUser redirect protection', () => {
it('should not loop infinitely on repeated redirects', async () => {
// Monkey-patch axios in the require cache to simulate redirect loop
let callCount = 0;
const axiosMock = {
get: async (url: string) => {
callCount += 1;
const isA = url === 'https://www.tradingview.com/';
return {
data: '<html>no auth here</html>',
headers: {
location: isA
? 'https://www.tradingview.com/accounts/signin/'
: 'https://www.tradingview.com/',
},
};
},
};

const axiosPath = require.resolve('axios');
const originalModule = require.cache[axiosPath];
require.cache[axiosPath] = {
id: axiosPath,
filename: axiosPath,
loaded: true,
exports: axiosMock,
} as any;

const miscPath = require.resolve('../src/miscRequests');
delete require.cache[miscPath];

try {
// eslint-disable-next-line global-require
const misc = require('../src/miscRequests');
await expect(
misc.getUser('fake_session', 'fake_signature'),
).rejects.toThrow('Too many redirects');

expect(callCount).toBeGreaterThan(0);
expect(callCount).toBeLessThanOrEqual(10);
} finally {
if (originalModule) require.cache[axiosPath] = originalModule;
else delete require.cache[axiosPath];
delete require.cache[miscPath];
}
}, 5000);
});
Loading