Maybe fix EventSource reconnect

This commit is contained in:
Samuel Elliott
2023-07-24 17:09:38 +01:00
parent fbc1bc1682
commit cd6f614a99

View File

@@ -30,8 +30,12 @@ type Listener<T extends string> =
[type: T, handler: (event: MessageEvent<string>) => void];
export default class EventSource {
protected static connections = new Set<EventSource>();
protected _connecting: Promise<Response> | null = null;
protected _response: Response | null = null;
protected _controller: AbortController | null = null;
protected _reconnect_timeout: NodeJS.Timeout | null = null;
protected _closed = false;
protected _id: string | null = null;
@@ -55,6 +59,26 @@ export default class EventSource {
if (init?.authorisation) this._authorisation = init.authorisation;
if (init?.useragent) this._useragent = init.useragent;
Object.defineProperty(this, '_connecting', {enumerable: false});
Object.defineProperty(this, '_response', {enumerable: false});
Object.defineProperty(this, '_controller', {enumerable: false});
Object.defineProperty(this, '_reconnect_timeout', {enumerable: false});
Object.defineProperty(this, '_closed', {enumerable: false});
Object.defineProperty(this, '_id', {enumerable: false});
Object.defineProperty(this, '_retry_after', {enumerable: false});
Object.defineProperty(this, '_authorisation', {enumerable: false});
Object.defineProperty(this, '_useragent', {enumerable: false});
Object.defineProperty(this, 'onerror', {enumerable: false, writable: true});
Object.defineProperty(this, 'onmessage', {enumerable: false, writable: true});
Object.defineProperty(this, 'onopen', {enumerable: false, writable: true});
Object.defineProperty(this, 'onAnyMessage', {enumerable: false, writable: true});
Object.defineProperty(this, '_listeners', {enumerable: false});
Object.defineProperty(this, '_message_event', {enumerable: false});
Object.defineProperty(this, '_message_data', {enumerable: false});
Object.defineProperty(this, '_message_id', {enumerable: false});
EventSource.connections.add(this);
this._connect();
}
@@ -96,6 +120,8 @@ export default class EventSource {
headers.append('Last-Event-Id', this._id);
}
debug('Connecting', this);
return fetch(this.url, {
headers,
signal,
@@ -104,16 +130,38 @@ export default class EventSource {
}
protected _connect() {
const controller = new AbortController();
if (this._closed || this._connecting) {
return;
}
this._fetch(controller.signal).then(async response => {
if (this._closed || this._response) {
if (this._reconnect_timeout) {
clearTimeout(this._reconnect_timeout);
this._reconnect_timeout = null;
}
this._controller?.abort();
const controller = new AbortController();
const connecting = this._fetch(controller.signal);
this._response = null;
this._controller = controller;
this._connecting = connecting;
connecting.then(async response => {
const url = new URL(this.url);
url.search = '';
url.hash = '';
debug('fetch %s %s, response %s', 'GET', url, response.status);
if (this._closed || this._controller !== controller) {
controller.abort();
return;
}
this._response = response;
this._controller = controller;
this._connecting = null;
if (!response.ok) {
debug('Non-200 response code', await response.text());
@@ -133,9 +181,6 @@ export default class EventSource {
return this._handleConnectionClosed();
}
const url = new URL(this.url);
url.search = '';
url.hash = '';
debug('Connected to %s', url);
const event = new Event('open');
@@ -255,14 +300,17 @@ export default class EventSource {
}
const wait = Math.max(1000, this._retry_after ?? 0);
await new Promise(rs => setTimeout(rs, wait));
this._connect();
clearTimeout(this._reconnect_timeout!);
this._reconnect_timeout = setTimeout(() => this._connect(), wait);
}
close() {
debug('Closing', this);
this._closed = true;
this._controller?.abort();
EventSource.connections.delete(this);
}
dispatchEvent(event: Event) {