In this second part, 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.

Part 2: the basic demo application, some client data added

Ingredient #1: the HTML page with JavaScript

The script now reads two data items from the ping event:

  • the time stamp, now sent from the server in proper ISO 8601 format
  • the peer data, which is the remote ip address and port number
<!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;
  const peer = JSON.parse(event.data).peer;
  newElement.textContent = `ping at ${time} from ${peer}`;
  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

The TIdHTTPServer subclass now contains a private method to provide client-specific data in the /sse resource.

  function TMySSEServer.BuildContentText(AContext: TIdContext): string;
  begin
    Result := '';
    repeat
      Result := Result + 'event: ping' + #13 +
        Format('data: {"time": "%s", "peer": "%s:%d"}',
        [DateToISO8601(Now, False), AContext.Binding.PeerIP,
        AContext.Binding.PeerPort]) + #13#13;
      Sleep(100);
    until Random < 0.8;
  end; 

The DoCommandGet method uses the BuildContentText function to provide the event data:

  procedure TMySSEServer.DoCommandGet(AContext: TIdContext;
    ARequestInfo: TIdHTTPRequestInfo;
    AResponseInfo: TIdHTTPResponseInfo);
  begin
    if ARequestInfo.Document = '/sse' then
    begin
      AResponseInfo.ContentType := 'text/event-stream';
      AResponseInfo.CacheControl := 'no-store';
      AResponseInfo.ContentText := BuildContentText(AContext);
    end
    else
    begin
      AResponseInfo.ContentType := 'text/html';
      AResponseInfo.ContentStream :=
        TFileStream.Create('index.html', fmOpenRead);
    end;
    AResponseInfo.CharSet := 'UTF-8';
  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 one or more events.

The ping event, which the server sends to the browser, now includes the server time in ISO 8601 format and the peer IP address and port.

Next part

In the next part, the data stream will be sent continuously.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s