diff options
author | zeripath <art27@cantab.net> | 2020-07-03 10:55:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-03 10:55:36 +0100 |
commit | ae56411e9f78415745c235fb92fbb7abc139b966 (patch) | |
tree | 20ce8e22055394dbf80ee9355cb5c1a03cb19479 /web_src/js/features/eventsource.sharedworker.js | |
parent | cd994f635427388d4e2abb6612e0b830a8f485b9 (diff) | |
download | gitea-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.js | 140 |
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(); + } +}; |