In this article, a server application uses the Indy HTTP server to provide a HTML page which uses SSE to update its content with data sent from the server.
Server-Sent Events (SSE) is a server push technology enabling a client to receive automatic updates from a server via an HTTP connection, and describes how servers can initiate data transmission towards clients once an initial client connection has been established.
Server-sent events, https://en.wikipedia.org/w/index.php?title=Server-sent_events&oldid=1093881969 (last visited Sept. 17, 2022).
Part 1: a very basic demo application
Ingredient #1: a HTML page with Javascript
<!DOCTYPE html>
<html>
<head>
<title>SSE example</title>
</head>
<body>
<script>
const evtSource = new EventSource("sse");
evtSource.addEventListener("ping", (event) => {
const newElement = document.createElement("li");
const eventList = document.getElementById("list");
const time = JSON.parse(event.data).time;
newElement.textContent = `ping at ${time}`;
eventList.appendChild(newElement);
});
</script>
<ul id="list">
</ul>
</body>
</html>
The code is based on the article Using server-sent events on MDN
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
Ingredient #2: server side code
In the example application, a TIdHTTPServer subclass is used to deliver the HTML document when a browser accesses it.
Note that the server has to keep connections alive, so the Server property KeepAlive must be set to true:
procedure Test;
begin
Server := TMySSEServer.Create;
try
Server.KeepAlive := True;
Server.Startup;
ReadLn;
finally
Server.Free;
end;
end;
The DoCommandGet method is overriden and looks like this:
procedure TMySSEServer.DoCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
S: TStream;
C: string;
begin
if ARequestInfo.Document = '/sse' then
begin
AResponseInfo.ContentType := 'text/event-stream';
AResponseInfo.CacheControl := 'no-store';
AResponseInfo.CharSet := 'UTF-8';
C := 'event: ping' + #13
+ Format('data: {"time": "%d"}', [GetTickCount])
+ #13#13;
AResponseInfo.ContentText := C;
AResponseInfo.ResponseNo := 200;
end
else
begin
S := TFileStream.Create('index.html', fmOpenRead);
AResponseInfo.ContentType := 'text/html';
AResponseInfo.ContentStream := S;
AResponseInfo.ResponseNo := 200;
end;
end;
Output

When the browser navigates to http://localhost
, the server will provide the HTML and the embedded JavaScript will start reading data from the address http://localhost/sse
(and receive only one event). As specified in the HTML spec for EventSource, the client will retry after some seconds:
Each
HTML – 9.2 Server-sent events (https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events)EventSource
object has the following associated with it:
– A reconnection time, in milliseconds. This must initially be an implementation-defined value, probably in the region of a few seconds.
Note: this is just the first step. In the next part, we will implement a continuos data stream.