Websocket no longer connecting
Websocket no longer connecting
10 Nov 2023, 15:53
Hi.
My websocket connecton to Open API is no longer managing to connect. It was fine up until a day or two ago. Below is the websocket service from my code, the connection url for cTrader is “wss://demo.ctraderapi.com:5036
" .. when logging this.ws I can see the readyStatus = 0 , which means its still connecting. I get no 'open' message and when i try to send a message it fails as the connection is closed. There is a “_closeCode: 1006,”
which is abnormal close but im not sure if the connection is actually opening and then closing at all as I am getting no error messages so I'm a bit stuck as to what to try next..
import { checkCtraderAccountAuthAndRefreshIfReq } from '$lib/auth/checkCtraderAccAuth';
import { getCTraderAuthTokensAndAccountFromSb } from '$lib/auth/initcTraderAuthTokens';
import { ExecutionEventHandler, cTraderSpotEventHandler } from '$lib/trader/cTraderEventHandlers';
import {
payloadTypeDict,
type EVENT_IDS,
type payloadTypeDictValues
} from '$lib/types/cTraderDictionaries';
import type { SpotEvent_ } from '$lib/types/cTraderEventTypes';
import type { BaseMessageType_ } from '$lib/types/openAPITypes';
import WebSocket from 'ws';
//https://github.com/websockets/ws/blob/HEAD/doc/ws.md#websocketsenddata-options-callback
//ws docs
export class WebSocketServiceClass {
private ws!: WebSocket;
// eslint-disable-next-line
private queue: Map<string, (data: any) => void>;
private connectionUrl: string;
private eventIds: EVENT_IDS[] = [];
public connected: boolean;
constructor() {
this.queue = new Map();
this.connectionUrl = import.meta.env.VITE_CTRADER_CONNECTION_URL; this.initializeWebSocket();
this.eventIds = [2107, 2120, 2123, 2126, 2131, 2132, 2141, 2147, 2148, 2155, 2164, 2171, 2172];
this.connected = false;
}
private initializeWebSocket() {
console.log('Initializing WebSocket connection to cTrader...', this.connectionUrl);
this.ws = new WebSocket(this.connectionUrl);
this.ws.on('message', this.handleMessage.bind(this));
this.ws.on('open', async () => {
this.connected = true;
console.log(`WebSocket connection opened to ${this.connectionUrl}`);
const authTokensAndAccount = await getCTraderAuthTokensAndAccountFromSb();
if (authTokensAndAccount?.account && authTokensAndAccount?.tokens?.accessToken) {
await checkCtraderAccountAuthAndRefreshIfReq(
authTokensAndAccount.account.ctidTraderAccountId
);
}
});
this.ws.on('close', (code, reason) => {
this.connected = false;
console.log('WebSocket connection closed: ', code, reason.toString());
// Attempt to restart the WebSocket connection
console.log('Attempting to reconnect websocket in 5 seconds...');
setTimeout(() => {
this.initializeWebSocket();
}, 5000);
});
this.ws.on('error', (error) => {
console.error('WebSocket error: ', error);
});
}
//eslint-disable-next-line
public async send(baseMessage: BaseMessageType_): Promise<any> {
const { payload, clientMsgId, payloadType } = baseMessage;
// Check if the WebSocket is in the "OPEN" state
if (this.ws.readyState === WebSocket.OPEN) {
// Construct the completed message with the supplied data
const completeMessage: BaseMessageType_ = {
clientMsgId,
payloadType,
payload: {
clientId: import.meta.env.VITE_OPEN_API_CLIENT_ID,
clientSecret: import.meta.env.VITE_OPEN_API_SECRET,
...payload
}
};
// eslint-disable-next-line
const promise = new Promise<any>((resolve) => {
this.queue.set(clientMsgId, resolve);
this.ws.send(JSON.stringify({ ...completeMessage }));
});
return promise;
} else {
// WebSocket is not in the "OPEN" state, handle the error accordingly
return Promise.reject(new Error('Owen: WebSocket is not connected.'));
}
}
private handleMessage(data: WebSocket.Data) {
const message = JSON.parse(data.toString());
const clientMsgId = message.clientMsgId;
const payloadType: payloadTypeDictValues | undefined = message.payloadType;
//divert event messages to the event handler
if (payloadType && this.eventIds.includes(payloadType as EVENT_IDS)) {
this.handleEventMessage(data);
return;
}
//Log errors
if (message.payload?.errorCode) {
message.payload?.errorCode === 'ALREADY_LOGGED_IN'
? console.log('Already logged in to cTracer account.')
: console.log(
`Error response from cTrader: Error code: ${message.payload.errorCode}, Description: ${message.payload.description}`
);
}
if (this.queue.has(clientMsgId)) {
const resolve = this.queue.get(clientMsgId);
if (resolve) {
resolve(message);
}
this.queue.delete(clientMsgId);
}
}
private handleEventMessage(data: WebSocket.Data) {
const message = JSON.parse(data.toString());
const payloadType: EVENT_IDS = message.payloadType;
switch (payloadType) {
case payloadTypeDict.SPOT_EVENT:
cTraderSpotEventHandler(message.payload as SpotEvent_['payload']); //ProtoOASpotEvent - (price stream event)
break;
case payloadTypeDict.SYMBOL_CHANGED_EVENT:
console.log('SYMBOL_CHANGED_EVENT - Symbols changed: ', message.payload?.symbolId);
break;
case payloadTypeDict.ORDER_ERROR_EVENT:
console.log(
`Order error event, message: ${message.payload?.description}, code: ${message.payload?.errorCode}`
);
break;
case payloadTypeDict.EXECUTION_EVENT:
ExecutionEventHandler(message);
break;
default:
console.log('Unhandled event message recieved from cTrader:', message);
}
}
}
This below is the websocket logged after instantiating it:
WebSocket {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
_binaryType: 'nodebuffer',
_closeCode: 1006,
_closeFrameReceived: false,
_closeFrameSent: false,
_closeMessage: <Buffer >,
_closeTimer: null,
_extensions: {},
_paused: false,
_protocol: '',
_readyState: 0,
_receiver: null,
_sender: null,
_socket: null,
_bufferedAmount: 0,
_isServer: false,
_redirects: 0,
_url: 'wss://demo.ctraderapi.com:5036/',
_req: ClientRequest {
_events: [Object: null prototype] {
error: [Function (anonymous)],
response: [Function (anonymous)],
upgrade: [Function (anonymous)]
},
_eventsCount: 3,
_maxListeners: undefined,
outputData: [ [Object] ],
outputSize: 233,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: null,
_header: 'GET / HTTP/1.1\r\n' +
'Sec-WebSocket-Version: 13\r\n' +
'Sec-WebSocket-Key: 0xpKKrylwaCDSf+pq2vZaA==\r\n' +
'Connection: Upgrade\r\n' +
'Upgrade: websocket\r\n' +
'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n' +
'Host: demo.ctraderapi.com:5036\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: undefined,
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/',
_ended: false,
res: null,
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'demo.ctraderapi.com',
protocol: 'https:',
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
'sec-websocket-version': [Array],
'sec-websocket-key': [Array],
connection: [Array],
upgrade: [Array],
'sec-websocket-extensions': [Array],
host: [Array]
}
},
[Symbol(kCapture)]: false
}