2017-05-25 12:30:08 +02:00
|
|
|
unit SSEControllerU;
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
MVCFramework, MVCFramework.Commons;
|
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
[MVCPath('/')]
|
|
|
|
TSSEController = class(TMVCController)
|
|
|
|
public
|
|
|
|
[MVCPath('/stocks')]
|
|
|
|
[MVCHTTPMethod([httpGET])]
|
|
|
|
[MVCProduces('text/event-stream')]
|
|
|
|
procedure Index;
|
|
|
|
|
|
|
|
protected
|
|
|
|
procedure OnBeforeAction(Context: TWebContext; const AActionName: string;
|
|
|
|
var Handled: Boolean); override;
|
|
|
|
procedure OnAfterAction(Context: TWebContext;
|
|
|
|
const AActionName: string); override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
|
|
|
MVCFramework.Logger, System.SysUtils, StorageU;
|
|
|
|
|
|
|
|
procedure TSSEController.Index;
|
|
|
|
var
|
|
|
|
lLastEventID: Integer;
|
|
|
|
lCurrentEventID: Integer;
|
|
|
|
lMessage: string;
|
|
|
|
begin
|
|
|
|
// wait a little bit
|
|
|
|
Sleep(1000 + Random(2000));
|
|
|
|
|
|
|
|
// retrieve the last id received by the client reading the request header.
|
|
|
|
lLastEventID := StrToIntDef(Context.Request.Headers['Last-Event-ID'], 0);
|
|
|
|
|
|
|
|
// get the next message to send based on the last id already received by the client
|
|
|
|
lMessage := GetNextDataToSend(lLastEventID, lCurrentEventID);
|
|
|
|
|
|
|
|
// setting up the correct SSE headers
|
|
|
|
ContentType := 'text/event-stream';
|
|
|
|
Context.Response.SetCustomHeader('Cache-Control', 'no-cache');
|
2017-07-05 00:17:46 +02:00
|
|
|
// WARNING!! keep-alive heaer has been set directly on the server!
|
|
|
|
// Context.Response.SetCustomHeader('Connection', 'keep-alive');
|
2017-05-25 12:30:08 +02:00
|
|
|
|
|
|
|
// render the response using SSE compliant data format
|
|
|
|
|
|
|
|
// current event id (the client will resend this number at the next request)
|
|
|
|
ResponseStream.Append('id: ' + IntToStr(lCurrentEventID) + #13);
|
|
|
|
|
|
|
|
// The browser attempts to reconnect to the source roughly 3 seconds after
|
|
|
|
// each connection is closed. You can change that timeout by including a line
|
|
|
|
// beginning with "retry:", followed by the number of milliseconds to wait
|
|
|
|
// before trying to reconnect.
|
|
|
|
ResponseStream.Append('retry: 100'#13);
|
|
|
|
|
|
|
|
ResponseStream.Append('event: stockupdate'#13);
|
|
|
|
|
|
|
|
// actual message
|
|
|
|
ResponseStream.Append('data: ' + lMessage + #13#13);
|
|
|
|
|
|
|
|
// render all the stuff
|
|
|
|
RenderResponseStream;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TSSEController.OnAfterAction(Context: TWebContext;
|
|
|
|
const AActionName: string);
|
|
|
|
begin
|
|
|
|
{ Executed after each action }
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TSSEController.OnBeforeAction(Context: TWebContext;
|
|
|
|
const AActionName: string; var Handled: Boolean);
|
|
|
|
begin
|
|
|
|
{ Executed before each action
|
|
|
|
if handled is true (or an exception is raised) the actual
|
|
|
|
action will not be called }
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|