Last active
November 19, 2022 00:11
-
-
Save jcubic/06837021480f4843f54e6896d7dfd213 to your computer and use it in GitHub Desktop.
Server-Sent Events with Service worker issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<meta charset="utf-8" /> | |
</head> | |
<body> | |
<button id="sse_start" disabled>Start</button> | |
<button id="sse_stop" disabled>Stop</button> | |
<pre id="output">Loading Service Worker</pre> | |
<script> | |
if ('serviceWorker' in navigator) { | |
var scope = location.pathname.replace(/\/[^\/]+$/, '/'); | |
navigator.serviceWorker.register('sw.js', { scope }) | |
.then(function(reg) { | |
reg.addEventListener('updatefound', function() { | |
var installingWorker = reg.installing; | |
console.log('A new service worker is being installed:', | |
installingWorker); | |
}); | |
// registration worked | |
console.log('Registration succeeded. Scope is ' + reg.scope); | |
sse_start.disabled = sse_stop.disabled = false; | |
set('Ready'); | |
}).catch(function(error) { | |
// registration failed | |
console.log('Registration failed with ' + error); | |
}); | |
} | |
function set(text) { | |
output.innerHTML = text; | |
} | |
function clear() { | |
output.innerHTML = ''; | |
} | |
function log(text) { | |
output.innerHTML += '\n' + text; | |
} | |
let see_source; | |
sse_start.addEventListener('click', () => { | |
see_source = new EventSource("./sse"); | |
clear(); | |
see_source.onmessage = event => { | |
log(event.data); | |
}; | |
}); | |
sse_stop.addEventListener('click', () => { | |
if (see_source) { | |
see_source.close(); | |
see_source = null; | |
} | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let timerId; | |
let count = 10; | |
function startStream(send) { | |
timerId = setInterval(function() { | |
if (--count <= 0) { | |
throw new Error('Nasty Error'); | |
} | |
const now = (new Date()).toString(); | |
send({ data: now }); | |
}, 1000); | |
} | |
function stopStream() { | |
clearInterval(timerId); | |
} | |
self.addEventListener('fetch', (event) => { | |
event.respondWith(new Promise((resolve, reject) => { | |
const req = event.request; | |
const url = new URL(req.url); | |
console.log({url}); | |
if (url.pathname.match(/sse/)) { | |
let send, close, defunct; | |
const stream = new ReadableStream({ | |
cancel() { | |
defunct = true; | |
stopStream(); | |
}, | |
start: controller => { | |
send = function(event) { | |
if (!defunct) { | |
const chunk = createChunk(event); | |
const payload = new TextEncoder().encode(chunk); | |
controller.enqueue(payload); | |
} | |
}; | |
close = function close() { | |
controller.close(); | |
stream = null; | |
stopStream(); | |
}; | |
} | |
}); | |
resolve(new Response(stream, { | |
headers: { | |
'Content-Type': 'text/event-stream; charset=utf-8', | |
'Transfer-Encoding': 'chunked', | |
'Connection': 'keep-alive' | |
} | |
})); | |
startStream(send); | |
} | |
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') { | |
return; | |
} | |
fetch(event.request).then(resolve).catch(reject); | |
})); | |
}); | |
function createChunk({ data, event, retry, id }) { | |
return Object.entries({ event, id, data, retry }) | |
.filter(([, value]) => value) | |
.map(([key, value]) => `${key}: ${value}`) | |
.join('\n') + '\n\n'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The code works fine, this is the proper way to implement Server-Sent Events inside the Service worker.
But there is a problem with:
Google Chrome: when setInterval throws an exception the script doesn't stop. And Server-Sent Events keep throwing errors in a loop.
Firefox: the Stream silently stops and the Server-Sent Event Stream is restarted.