|
import { readFileSync } from "node:fs"; |
|
import { createSecureServer } from "node:http2"; |
|
|
|
const MAX_DELAY = 5_000; |
|
|
|
const home = /* HTML */ ` |
|
<!doctype html> |
|
<html> |
|
<head> |
|
<title>SSE connection count test</title> |
|
<style> |
|
body { |
|
font-family: sans-serif; |
|
} |
|
.connection { |
|
width: 14rem; |
|
height: 2rem; |
|
box-sizing: border-box; |
|
border: 1px solid #ccc; |
|
padding: 0.25rem; |
|
overflow: hidden; |
|
white-space: nowrap; |
|
text-overflow: ellipsis; |
|
color: black; |
|
transition: background-color 4s ease; |
|
} |
|
.connection { |
|
margin: 4px 0; |
|
} |
|
.connecting { |
|
color: gray; |
|
} |
|
.connected { |
|
color: green; |
|
} |
|
.message { |
|
background-color: #00ff0099; |
|
transition: background-color 0s ease; |
|
} |
|
.disconnected, |
|
.error { |
|
color: red; |
|
} |
|
|
|
#connections { |
|
display: flex; |
|
flex-wrap: wrap; |
|
gap: 0.5rem; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<h1>SSE Connections</h1> |
|
<div id="connections"></div> |
|
|
|
<script> |
|
const container = document.getElementById("connections"); |
|
const COUNT = 105; |
|
|
|
for (let i = 1; i <= COUNT; i++) { |
|
const id = i; |
|
const div = document.createElement("div"); |
|
div.className = "connection connecting"; |
|
div.textContent = id + ": connecting..."; |
|
container.appendChild(div); |
|
|
|
const es = new EventSource("/sse/" + id); |
|
|
|
es.onmessage = (event) => { |
|
div.className = "connection connected message"; |
|
div.textContent = id + ": message: " + event.data; |
|
setTimeout(() => { |
|
div.className = "connection connected"; |
|
}, 10); |
|
}; |
|
|
|
es.onerror = () => { |
|
if (es.readyState === EventSource.CLOSED) { |
|
div.className = "connection disconnected"; |
|
div.textContent = id + ": disconnected"; |
|
} else { |
|
div.className = "connection error"; |
|
div.textContent = id + ": error"; |
|
} |
|
}; |
|
} |
|
</script> |
|
</body> |
|
</html> |
|
`; |
|
|
|
const notFound = ` |
|
<!doctype html> |
|
<html> |
|
<head> |
|
<title>Not found</title> |
|
</head> |
|
<body> |
|
<h1>Not found</h1> |
|
<a href="/">Go home</a> |
|
</body> |
|
</html> |
|
`; |
|
|
|
const server = createSecureServer( |
|
{ |
|
// Create with: |
|
// openssl req -x509 -newkey rsa:2048 -nodes -keyout server.key -out server.crt -days 365 |
|
key: readFileSync("server.key"), |
|
cert: readFileSync("server.crt"), |
|
}, |
|
(req, res) => { |
|
if (req.url === "/") { |
|
res.writeHead(200, { "content-type": "text/html" }); |
|
res.end(home); |
|
} else if (req.url?.startsWith("/sse/")) { |
|
const started = Date.now(); |
|
const id = req.url.split("/").pop(); |
|
|
|
res.writeHead(200, { |
|
"Content-Type": "text/event-stream", |
|
"Cache-Control": "no-cache", |
|
Connection: "keep-alive", |
|
"Access-Control-Allow-Origin": "*", |
|
}); |
|
|
|
let timer: any; |
|
function next() { |
|
const uptime = ((Date.now() - started) / 1000).toFixed(1); |
|
res.write(`data: ${id}: ${uptime}\n\n`); |
|
timer = setTimeout(next, Math.random() * MAX_DELAY); |
|
} |
|
next(); |
|
req.on("close", () => { |
|
clearTimeout(timer); |
|
}); |
|
} else { |
|
res.writeHead(404, { "content-type": "text/html" }); |
|
res.end(notFound); |
|
} |
|
}, |
|
); |
|
|
|
server.listen(8888); |