summaryrefslogtreecommitdiffstats
path: root/web_src/js/features/eventsource.sharedworker.js
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2020-07-03 10:55:36 +0100
committerGitHub <noreply@github.com>2020-07-03 10:55:36 +0100
commitae56411e9f78415745c235fb92fbb7abc139b966 (patch)
tree20ce8e22055394dbf80ee9355cb5c1a03cb19479 /web_src/js/features/eventsource.sharedworker.js
parentcd994f635427388d4e2abb6612e0b830a8f485b9 (diff)
downloadgitea-ae56411e9f78415745c235fb92fbb7abc139b966.tar.gz
gitea-ae56411e9f78415745c235fb92fbb7abc139b966.zip
Move EventSource to SharedWorker (#12095)
Move EventSource to use a SharedWorker. This prevents issues with HTTP/1.1 open browser connections from preventing gitea from opening multiple tabs. Also allow setting EVENT_SOURCE_UPDATE_TIME to disable EventSource updating Fix #11978 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'web_src/js/features/eventsource.sharedworker.js')
-rw-r--r--web_src/js/features/eventsource.sharedworker.js140
1 files changed, 140 insertions, 0 deletions
diff --git a/web_src/js/features/eventsource.sharedworker.js b/web_src/js/features/eventsource.sharedworker.js
new file mode 100644
index 0000000000..7dccd8994e
--- /dev/null
+++ b/web_src/js/features/eventsource.sharedworker.js
@@ -0,0 +1,140 @@
+self.name = 'eventsource.sharedworker.js';
+
+const sourcesByUrl = {};
+const sourcesByPort = {};
+
+class Source {
+ constructor(url) {
+ this.url = url;
+ this.eventSource = new EventSource(url);
+ this.listening = {};
+ this.clients = [];
+ this.listen('open');
+ this.listen('logout');
+ this.listen('notification-count');
+ this.listen('error');
+ }
+
+ register(port) {
+ if (!this.clients.includes(port)) return;
+
+ this.clients.push(port);
+
+ port.postMessage({
+ type: 'status',
+ message: `registered to ${this.url}`,
+ });
+ }
+
+ deregister(port) {
+ const portIdx = this.clients.indexOf(port);
+ if (portIdx < 0) {
+ return this.clients.length;
+ }
+ this.clients.splice(portIdx, 1);
+ return this.clients.length;
+ }
+
+ close() {
+ if (!this.eventSource) return;
+
+ this.eventSource.close();
+ this.eventSource = null;
+ }
+
+ listen(eventType) {
+ if (this.listening[eventType]) return;
+ this.listening[eventType] = true;
+ const self = this;
+ this.eventSource.addEventListener(eventType, (event) => {
+ self.notifyClients({
+ type: eventType,
+ data: event.data
+ });
+ });
+ }
+
+ notifyClients(event) {
+ for (const client of this.clients) {
+ client.postMessage(event);
+ }
+ }
+
+ status(port) {
+ port.postMessage({
+ type: 'status',
+ message: `url: ${this.url} readyState: ${this.eventSource.readyState}`,
+ });
+ }
+}
+
+self.onconnect = (e) => {
+ for (const port of e.ports) {
+ port.addEventListener('message', (event) => {
+ if (event.data.type === 'start') {
+ const url = event.data.url;
+ if (sourcesByUrl[url]) {
+ // we have a Source registered to this url
+ const source = sourcesByUrl[url];
+ source.register(port);
+ sourcesByPort[port] = source;
+ return;
+ }
+ let source = sourcesByPort[port];
+ if (source) {
+ if (source.eventSource && source.url === url) return;
+
+ // How this has happened I don't understand...
+ // deregister from that source
+ const count = source.deregister(port);
+ // Clean-up
+ if (count === 0) {
+ source.close();
+ sourcesByUrl[source.url] = null;
+ }
+ }
+ // Create a new Source
+ source = new Source(url);
+ source.register(port);
+ sourcesByUrl[url] = source;
+ sourcesByPort[port] = source;
+ return;
+ } else if (event.data.type === 'listen') {
+ const source = sourcesByPort[port];
+ source.listen(event.data.eventType);
+ return;
+ } else if (event.data.type === 'close') {
+ const source = sourcesByPort[port];
+
+ if (!source) return;
+
+ const count = source.deregister(port);
+ if (count === 0) {
+ source.close();
+ sourcesByUrl[source.url] = null;
+ sourcesByPort[port] = null;
+ }
+ return;
+ } else if (event.data.type === 'status') {
+ const source = sourcesByPort[port];
+ if (!source) {
+ port.postMessage({
+ type: 'status',
+ message: 'not connected',
+ });
+ return;
+ }
+ source.status(port);
+ return;
+ } else {
+ // just send it back
+ port.postMessage({
+ type: 'error',
+ message: `received but don't know how to handle: ${event.data}`,
+ });
+ return;
+ }
+ });
+ port.start();
+ }
+};