Last active
March 31, 2023 17:14
-
-
Save FND/bd5e0307dc762e0fcd428c675a6e64e0 to your computer and use it in GitHub Desktop.
SSE client & server
This file contains hidden or 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 lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>exploring browsers' limits WRT event frequency</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<style> | |
*, | |
*::before, | |
*::after { | |
box-sizing: border-box; | |
} | |
body { | |
--h-size: 60ch; | |
--spacing: 0.5rem; | |
max-width: calc(var(--h-size) + 2 * var(--spacing)); | |
margin: 1rem auto; | |
padding-left: var(--spacing); | |
padding-right: var(--spacing); | |
font-family: system-ui, sans-serif; | |
line-height: 1.5; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>exploring browsers' limits WRT event frequency</h1> | |
<p>receiving data via Server-Sent Events</p> | |
<p>check console for details</p> | |
<script type="module"> | |
let URI = "/stream"; | |
let THRESHOLD = 100; | |
let count = 0; | |
let t0; | |
console.log("trying to establish SSE connection (this might take a few seconds)"); | |
let sse = new EventSource(URI); | |
sse.addEventListener("open", ev => { | |
console.log("[SSE] connection established"); | |
}); | |
sse.addEventListener("message", onMessage); | |
sse.addEventListener("start", onStatus); | |
sse.addEventListener("end", onStatus); | |
sse.addEventListener("error", ev => { | |
console.error("[SSE] unexpected error:", ev); | |
}); | |
function onStatus(ev) { | |
console.log(`[SSE] ${ev.type}:`, ev.data); | |
} | |
function onMessage(ev) { | |
if(count === 0) { | |
t0 = Date.now(); | |
var delta = 0; | |
} else { | |
delta = Date.now() - t0; | |
} | |
count++; | |
if(delta > THRESHOLD) { | |
console.log(`[SSE] received ${count} messages in ${delta} ms`); | |
console.log(`[SSE] ... latest message was`, ev.data); | |
count = 0; | |
} | |
} | |
</script> | |
</body> | |
</html> |
This file contains hidden or 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
{ | |
"scripts": { | |
"start": "node ./server.js" | |
}, | |
"type": "module" | |
} |
This file contains hidden or 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
import http from "node:http"; | |
import { readFile } from "node:fs/promises"; | |
import { fileURLToPath } from "node:url"; | |
import { resolve, dirname } from "node:path"; | |
let HOST = "localhost"; | |
let PORT = 3000; | |
let HTML = "index.html"; | |
let COUNT = 100 * 1000; | |
let DEPTH = 2; | |
let BREADTH = 3; | |
let ROOT = dirname(fileURLToPath(import.meta.url)); | |
let CRLF = "\r\n"; | |
let server = http.createServer(handleRequest).listen(PORT, HOST, () => { | |
let { address, port } = server.address(); | |
console.error(`→ http://${address}:${port}`); | |
}); | |
async function handleRequest(req, res) { | |
switch(req.url) { | |
case "/": { | |
let filepath = resolve(ROOT, HTML); | |
res.writeHead(200, { "Content-Type": "text/html" }); | |
let html = await readFile(filepath, "utf8"); | |
res.end(html); | |
} | |
case "/stream": | |
return sse(req, res); | |
default: | |
res.writeHead(404); | |
res.end("not found"); | |
return; | |
} | |
} | |
function sse(req, res) { | |
let { socket } = res; | |
socket.write("HTTP/1.1 200 OK" + CRLF); | |
socket.write("Content-Type: text/event-stream" + CRLF); | |
socket.write("Cache-Control: no-cache" + CRLF); | |
socket.write(CRLF); | |
socket.write(` | |
event: start | |
data: ${new Date().toISOString()} | |
`.trim() + "\n\n"); | |
let events = generateEvents(COUNT, { breadth: BREADTH, depth: DEPTH }); | |
console.error("emitting data"); | |
let t0 = Date.now(); | |
for(let ev of events) { | |
socket.write(ev); | |
} | |
console.error(`done; took ${Date.now() - t0} ms`); | |
socket.write(` | |
event: end | |
data: ${new Date().toISOString()} | |
`.trim() + "\n\n"); | |
} | |
function generateEvents(count, { breadth, depth }) { | |
console.error("generating data", { count, breadth, depth }); | |
let items = []; | |
let t0 = Date.now(); | |
for(let i = 0; i < count; i++) { | |
let item = generateObject(breadth, depth); | |
item = `event: message\ndata: ${JSON.stringify(item)}\n\n`; | |
items.push(item); | |
} | |
console.error(`... took ${Date.now() - t0} ms`); | |
return items; | |
} | |
function randomKey(len) { | |
return new Array(len).fill(0). | |
map(() => Math.floor(Math.random() * 16).toString(16)). | |
join(""); | |
} | |
// adapted from surma <https://bugzilla.mozilla.org/show_bug.cgi?id=1564880> | |
function generateObject(breadth, depth, len = 16) { | |
if(depth == 0) { | |
return randomKey(len); | |
} | |
let obj = {}; | |
for(let i = 0; i < breadth; i++) { | |
obj[randomKey(len)] = generateObject(breadth, depth - 1, len); | |
} | |
return obj; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment