Your IP : 216.73.216.86


Current Path : /var/www/homesaver/www/bitrix/modules/pull/install/js/pull/connector/src/transport/
Upload File :
Current File : /var/www/homesaver/www/bitrix/modules/pull/install/js/pull/connector/src/transport/longpolling.js

/* eslint-disable @bitrix24/bitrix24-rules/no-native-events-binding */

import { ConnectionType } from '../../../client/src/consts';
import { AbstractConnector } from './base';
import { getDateForLog, isNotEmptyString } from '../../../util/src/util';
import type { ConnectorOptions } from './base';

const LONG_POLLING_TIMEOUT = 60;

type LongPollingConnectorOptions = ConnectorOptions & {
	isBinary: boolean,
	getPublicationPath: () => string,
}

export class LongPollingConnector extends AbstractConnector
{
	connectionType = ConnectionType.LongPolling;
	active = false;
	requestTimeout = null;
	failureTimeout = null;
	requestAborted = false;

	constructor(config: LongPollingConnectorOptions)
	{
		super(config);

		this.xhr = this.createXhr();
		this.isBinary = config.isBinary;
	}

	createXhr(): XMLHttpRequest
	{
		const result = new XMLHttpRequest();
		if (this.isBinary)
		{
			result.responseType = 'arraybuffer';
		}
		result.addEventListener('readystatechange', this.onXhrReadyStateChange.bind(this));

		return result;
	}

	connect()
	{
		this.active = true;
		this.performRequest();
	}

	disconnect(code, reason)
	{
		this.active = false;

		if (this.failureTimeout)
		{
			clearTimeout(this.failureTimeout);
			this.failureTimeout = null;
		}

		if (this.requestTimeout)
		{
			clearTimeout(this.requestTimeout);
			this.requestTimeout = null;
		}

		if (this.xhr)
		{
			this.requestAborted = true;
			this.xhr.abort();
		}

		this.disconnectCode = code;
		this.disconnectReason = reason;
		this.connected = false;
	}

	performRequest()
	{
		if (!this.active)
		{
			return;
		}

		if (!this.path)
		{
			throw new Error('Long polling connection path is not defined');
		}

		if (this.xhr.readyState !== 0 && this.xhr.readyState !== 4)
		{
			return;
		}

		clearTimeout(this.failureTimeout);
		clearTimeout(this.requestTimeout);

		this.failureTimeout = setTimeout(
			() => {
				this.connected = true;
			},
			5000,
		);
		this.requestTimeout = setTimeout(this.onRequestTimeout.bind(this), LONG_POLLING_TIMEOUT * 1000);

		this.xhr.open('GET', this.path);
		this.xhr.send();
	}

	onRequestTimeout()
	{
		this.requestAborted = true;
		this.xhr.abort();
		this.performRequest();
	}

	onXhrReadyStateChange()
	{
		if (this.xhr.readyState === 4)
		{
			if (!this.requestAborted || this.xhr.status === 200)
			{
				this.onResponse(this.xhr.response);
			}
			this.requestAborted = false;
		}
	}

	/**
	 * Sends some data to the server via http request.
	 */
	send(buffer: ArrayBuffer): void
	{
		const path = this.parent.getPublicationPath();
		if (!path)
		{
			console.error(`${getDateForLog()}: Pull: publication path is empty`);

			return;
		}

		const xhr = new XMLHttpRequest();
		xhr.open('POST', path);
		xhr.send(buffer);
	}

	onResponse(response)
	{
		if (this.failureTimeout)
		{
			clearTimeout(this.failureTimeout);
			this.failureTimeout = 0;
		}

		if (this.requestTimeout)
		{
			clearTimeout(this.requestTimeout);
			this.requestTimeout = 0;
		}

		if (this.xhr.status === 200)
		{
			this.connected = true;
			if (isNotEmptyString(response) || (response instanceof ArrayBuffer))
			{
				this.callbacks.onMessage(response);
			}
			else
			{
				this.parent.session.mid = null;
			}
			this.performRequest();
		}
		else if (this.xhr.status === 304)
		{
			this.connected = true;
			if (this.xhr.getResponseHeader('Expires') === 'Thu, 01 Jan 1973 11:11:01 GMT')
			{
				const lastMessageId = this.xhr.getResponseHeader('Last-Message-Id');
				if (isNotEmptyString(lastMessageId))
				{
					this.parent.setLastMessageId(lastMessageId);
				}
			}
			this.performRequest();
		}
		else
		{
			this.callbacks.onError('Could not connect to the server');
			this.connected = false;
		}
	}
}