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