A new Indy HTTP client / JAX-RS server example is now available on GitHub. The server side generates Server-sent events. Server-sent events (SSE) is a technology enabling a browser to receive automatic updates from a server via HTTP connection.

The example code uses TIdHTTP and TIdEventStream to connect to the server, and writes the incoming events to the console window.

indy-sse-jaxrs

Requirements

  • Delphi or Lazarus IDE
  • Indy 10.6.2
  • Java IDE
  • Java EE 8 application server

Client

The SSE client setup in the TIndySSEClient.Create creates a TIdHTTP instance and a TIdEventStream instance.
As recommended, the Accept header is set to 'text/event-stream'. It also sets the Cache-Control header to 'no-store' to prevent proxy servers from caching the result.
Our event handler TIndySSEClient.MyOnWrite is assigned to the TIdEventStreamOnWrite property.

constructor TIndySSEClient.Create;
begin
  inherited Create;

  SSE_URL := URL;

  EventStream := TIdEventStream.Create;
  EventStream.OnWrite := MyOnWrite;

  IdHTTP := TIdHTTP.Create;
  IdHTTP.Request.Accept := 'text/event-stream';
  IdHTTP.Request.CacheControl := 'no-store';
end;

The OnWrite handler decodes the UTF-8 encoded event data and writes it to the console:

procedure TIndySSEClient.MyOnWrite;
begin
  WriteLn('Received ' + IntToStr(Length(ABuffer)) + ' bytes');
  WriteLn;
  WriteLn(IndyTextEncoding_UTF8.GetString(ABuffer));
  ...
end;

Server (main REST method)

The server generates the server-sent event and places a Stock instance in its data part:

  @GET
  @Path("prices")
  @Produces(MediaType.SERVER_SENT_EVENTS)
  public void getStockPrices(@Context SseEventSink sseEventSink) {
    int lastEventId = 1;

    while (running) {
      Stock stock = getNextTransaction();

      System.out.println("Send event ...");
      OutboundSseEvent sseEvent = this.eventBuilder
              .name("stock")
              .id(String.valueOf(lastEventId))
              .mediaType(MediaType.APPLICATION_JSON_TYPE)
              .data(Stock.class, stock)
              .reconnectDelay(3000)
              .comment("price change")
              .build();

      sseEventSink.send(sseEvent);
      lastEventId++;
    }
    sseEventSink.close();
  }

Additional notes

  • this is the first draft of a SSE client, which does not support features such as reconnect
  • the example assumes that every call of MyOnWrite contains exactly one event, teminated by a sequence of two line separators (cr lf / cr / lf) so that no extra code is required to do proper event stream parsing

Full source code

https://github.com/michaelJustin/indy-sse-jaxrs

References

4 thoughts on “Consuming Server-Sent Events (SSE) with Indy TIdHTTP and TIdEventStream

  1. NOTE: this approach works ONLY if 1) the server uses HTTP’s “chunked” transfer encoding when sending push events, 2) each chunk is a self-contained event, and 3) a TStream is passed to TIdHTTP for it to write to, otherwise the OnChunkedReceived event will not be called.

    There are other ways to handle server-side pushes, such as with MIME’s “multipart/x-mixed-replace” media type, which will not work with the OnChunkReceived event, you would have to enable TIdHTTP’s hoNoReadMultipartMIME flag and then manually read the MIME data after TIdHTTP has fully exited (see https://www.indyproject.org/2014/03/05/new-tidhttp-honoreadmultipartmime-flag/).

    1. Many thanks! I have updated the code to use TIdEventStream instead of OnChunkReceived. I understand that it is not guaranteed to receive acomplete event in a single TIdEventStream.OnWrite, need to read about chunked HTTP transfer, and SSE implementations.

  2. Nice article. But, how we generate server-sent events with indy server? Is it possible?

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