Question

Using Delphi 2010 and Indy10

I am attempting to read Server Sent Events from Delphi. I have managed to create a thread which will subscribe to a connection on my server (Python/Flask) and capture the events. A thread is needed because the HTTP.Get is blocking forever. I realize that the entire protocol is not implemented, but I am only interested in receiving short strings broadcast from the server. The following code works, however I have not been able to figure out how to cleanly stop the thread and free the http_client and stream when stopping my program.

On the server side I get ""An existing connection was forcibly close by the remote host"" and my program is indicating memory leaks. I have tried many different combinations of trying to close the http connection at various points (onTerminate,Destroy) and I can't get anything to work. Any insights or examples would be greatly appreciated.

Here is the relevant code I have so far:

TSSEThread = class(TThread)
   private
     url: String;
     stream: TMemoryStream;
     http_client: TidHTTP;
     procedure DoOnWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
   public
     constructor Create(CreateSuspended: Boolean; url: String);
     procedure Execute; override;
end;


constructor TSSEThread.Create(CreateSuspended: Boolean; url: String);
begin
  inherited Create(CreateSuspended); // sending False here
  Self.FreeOnTerminate := False;
  Self.url := url;
end;

procedure TSSEThread.Execute;
begin
  inherited;
  try
    stream := TMemoryStream.Create;
    http_client := TIdHTTP.Create(nil);
    http_client.OnWork := DoOnWork;
    http_client.Request.CacheControl := 'no-cache';
    http_client.Request.Accept := 'text/event-stream';
    http_client.Response.KeepAlive := True;
    while not Terminated do
      try
        http_client.Get(self.url,stream);
      except
        on E: Exception do begin
          try
            http_client.Disconnect;
          except
          end;
          if http_client.IOHandler <> nil then http_client.IOHandler.InputBuffer.Clear;
          sleep(3000);
        end;
      end;
    finally
      http_client.Free;
      stream.Free;
    end;
end;

procedure TSSEThread.DoOnWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
  msg: string;
begin
  if self.Terminated then Exit;
  SetString(msg,PAnsiChar(stream.Memory),stream.Size);
  stream.Position := 0;
  if Pos('data:',msg) > 0 then
  begin
    // Handle msg here. This works fine.
  end;
end;
Was it helpful?

Solution

In your OnWork event handler, when the thread's Terminated property is true, raise an exception (such as by calling SysUtils.Abort()) instead of calling Exit. That exception will abort the HTTP request and close the connection, then propegate to Execute(), where you catch it and check Terminated again to break your while loop and let your finally block free the TIdHTTP object before exiting Execute() to terminate the thread.

procedure TSSEThread.DoOnWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
  msg: string;
begin
  //if self.Terminated then Exit;
  if self.Terminated then SysUtils.Abort;
  ...
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top