How to: Server-Sent Events with Indy HTTP Server (part 2)

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.

How to: Server-Sent Events with Indy HTTP Server (part 1)

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 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.

HTML – 9.2 Server-sent events (https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events)

Note: this is just the first step. In the next part, we will implement a continuos data stream.

ScroogeXHTML for Object Pascal 8.2 released

ScroogeXHTML for Object Pascal is a library which supports a subset of the Rich Text Format (RTF) standard. It converts RTF to HTML5 and XHTML stand-alone documents, or to fragments which can be embedded in other documents. The library is compatible with Delphi 2009+ and Free Pascal 3.2.0. The 8.2 release includes small improvements in the core library and documentation about the new post process API. The installer is now digitally signed.

Resources

Other platforms

ScroogeXHTML is also available for the Java platform.

scrooge_portrait_logo_2016

ScroogeXHTML for Object Pascal 8.1 released

ScroogeXHTML for Object Pascal is a library which supports a subset of the Rich Text Format (RTF) standard. It converts RTF to HTML5 and XHTML stand-alone documents, or to fragments which can be embedded in other documents. The library is compatible with Delphi 2009+ and with Free Pascal 3.2.0. The 8.1 release includes small improvements in the core library and in the demo application.

Resources

Licensing

ScroogeXHTML licenses use a subscription model, which can be cancelled any time without losing any distribution rights for applications using the library.

Other platforms

ScroogeXHTML is also available for the Java platform.

scrooge_portrait_logo_2016

Indy FTP LIST timeout with Delphi 11 in active mode (solved)

In the Indy issue tracker, a bug was reported which first appeared with Delphi 11.

FTP LIST timeout with latest Delphi 11 (and idFTP)

It is possible to reproduce the bug not only with Delphi 11 but also with Free Pascal. Reportedly the timeout also occurs with Delphi 10.4.

If you want to reproduce it with your version of Delphi (or Free Pascal), you may take a small test project from https://github.com/IndySockets/Indy/issues/377#issuecomment-932632490 and configure it to connect with a local or public FTP server.

Test with Delphi 2009 and Free Pascal 3.2.0 (Lazarus 2.0.12) on Windows 10

With Delphi 2009 and Free Pascal 3.2.0, I reproduced a read timeout in line 2134:

if LReadList.ContainsSocket(LDataSocket) then
begin
  LPortSv.Listen(0);
  Self.GetResponse([125, 150, 154]);  <--------------- read timeout
end else
begin
  Self.GetResponse([125, 150, 154]);
end;

STAT> Connected.
RECV> 220 Microsoft FTP Service
SENT> HOST test.rebex.net
RECV> 504 Server cannot accept argument.
SENT> USER demo
RECV> 331 Password required for demo.
SENT> PASS password
RECV> 230 User logged in.
SENT> FEAT
RECV> 211-Extended features supported:
RECV> LANG EN* UTF8 AUTH TLS;TLS-C;SSL;TLS-P; PBSZ PROT C;P; CCC HOST SIZE MDTM REST STREAM211 END
SENT> OPTS UTF8 ON
RECV> 200 OPTS UTF8 command successful – UTF8 encoding now ON.
SENT> TYPE A
RECV> 200 Type set to A.
SENT> SYST
RECV> 215 Windows_NT
SENT> TYPE A
RECV> 200 Type set to A.
SENT> PORT 192,168,178,20,204,119
RECV> 200 PORT command successful.
SENT> LIST
STAT> Disconnected.
Exception at 000000010001DCD9: EIdReadTimeout:
Read timed out.

Tested with servers

  • test.rebex.net
  • ftp.dlptest.com

Call for testers

It would be interesting to confirm if the bug only appears in some specific versions of Delphi and Free Pascal. If you can reproduce it, you may comment here.

Resolved

The issue is resolved now

ScroogeXHTML for Object Pascal 8.0 released

ScroogeXHTML for Object Pascal is a library which supports a subset of the Rich Text Format (RTF) standard. It converts RTF to HTML5 and XHTML standalone documents, or to fragments which can be embedded in other documents. ScroogeXHTML is compatible with Delphi 2009+ and tested with Free Pascal 3.2.0.

Version 8.0 is a major release which introduces new options, including customizable post-processing of the (internal) document tree before the final conversion step, and a customizable hyperlink URI builder class.

Resources

ScroogeXHTML is also available for the Java platform.

scrooge_portrait_logo_2016

ScroogeXHTML for Object Pascal 7.3 released

ScroogeXHTML for Object Pascal is a library which supports a subset of the Rich Text Format (RTF) standard. It converts RTF to HTML5 and XHTML standalone documents, or to fragments which can be embedded in other documents. ScroogeXHTML converts text attributes including background and highlight colors, paragraph alignment (left, right, centered, justified) and indent (left, right, first line).
Unicode conversion allows multi-language documents, including simplified and traditional Chinese, Korean and Japanese. Compatible with Delphi 2009+ and tested with Free Pascal 3.0.4 / 3.2.0.

  • Version 7.3 is mainly a maintenance release which fixed several minor issues.
  • Version 7.2 introduced support for embedded PNG and JPEG images using the data URI scheme.

Resources

ScroogeXHTML is also available for the Java platform.

scrooge_portrait_logo_2016

Discover ActiveMQ brokers with Indy 10.6 and IP multicast

This article shows how IP multicast can be used to discover Apache ActiveMQ message broker instances in the local network using Indy. Example output:

Screenshot 2021-08-14 164137

With the code below, an application can list all ActiveMQ brokers, see their status, and get the IP addresses, protocols, and port numbers of discoverable transport connectors. Transport connectors are discoverable, if their broker.xml configuration entry includes a discoveryUri attribute, for example discoveryUri=”multicast://default”.

Continue reading “Discover ActiveMQ brokers with Indy 10.6 and IP multicast”

ScroogeXHTML for Delphi 7.1 released

Version 7.1 of ScroogeXHTML for Delphi is a maintenance release which contains minor improvements and fixes. Full release notes can be found here.

Resources

ScroogeXHTML is also available for the Java platform.

scrooge_portrait_logo_2016

Habari Client libraries release 2020.04

Habarisoft released the 2020.04 version of its Object Pascal STOMP client libraries for application integration with open source message brokers Apache ActiveMQ, Artemis, OpenMQ and RabbitMQ. The release includes

About Habari Client libraries

habari_logo_2016Habari Client libraries enable Object Pascal applications to take advantage of message broker / message queue technology – which is distributed, loosely coupled, reliable and asynchronous – to build integrated systems, using peer-to-peer and publish-subscribe communication models.