I have to create an embedded HTTP server in our application. This must be created for interprocess communication, because the other side can call only WebService.
We don't need to process more requests at once, but I must use the libraries and DB connection from the main thread.
The Delphi version is Seattle, the Indy version is 10 (internal).
As I know the IdHTTPServer uses threads for connections - as formerly. But the new event handler don't pass the Thread directly - so I can't call TThread.Synchronize as N years before. And I didn't find any way to get the thread.
Somewhere I've read that TIdSync or TIdNotify classes could help me. I can't find any complete example to see how.
See this simple code. Is it enough to do my work in main thread?
procedure TForm5.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo);
begin
FURI := ARequestInfo.URI; // Store the URI for main thread access
FResult := '';
TIdSync.SynchronizeMethod(Self.ProcessIt); // Call sync to process in VCL thread
AResponseInfo.ContentText := FResult; // This variable contains the result
end;
Or I do something wrong?
Thank you for the help!
dd
There is a way to get access to the underlying TThread
behind a TIdContext
- typecast the TIdContext.Yarn
property to TIdYarnOfThread
and then access its Thread
property, eg:
procedure TForm5.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
Thread: TThread;
begin
Thread := (AContext.Yarn as TIdYarnOfThread).Thread;
FURI := ARequestInfo.URI;
FResult := '';
Thread.Synchronize(ProcessIt);
AResponseInfo.ContentText := FResult;
end;
But, you don't actually need that in your situation, because TThread.Synchronize()
(and TThread.Queue()
) has class
method overloads, so you don't need a TThread
object to call it, eg:
procedure TForm5.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
FURI := ARequestInfo.URI;
FResult := '';
TThread.Synchronize(nil, ProcessIt);
AResponseInfo.ContentText := FResult;
end;
TIdSync
(and TIdNotify
) can indeed be used instead, and what you have is a good start for that, it just needs to be fleshed out a little more so that access to your FURI
and FResult
variables is synchronized as well, in case you ever need to handle more than 1 client connection at a time and thus would need to avoid concurrent access to the variables, eg:
type
TIdProcessItSync = class(TIdSync)
URI: string;
Content: string;
protected
procedure DoSynchronize; override;
end;
procedure TIdProcessItSync.DoSynchronize;
begin
Content := Form5.ProcessIt(URI);
end;
function TForm5.ProcessIt(const URI: string): String;
begin
Result := ...;
end;
procedure TForm5.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
Sync: TIdProcessItSync;
begin
Sync := TIdProcessItSync.Create;
try
Sync.URI := ARequestInfo.URI;
Sync.Synchronize;
AResponseInfo.ContentText := Sync.Content;
finally
Sync.Free;
end;
end;
That being said, TIdSync
(and TIdNotify
) is deprecated in favor of TThread
's class
method overloads that support anonymous procedures, eg:
function TForm5.ProcessIt(const URI: string): String;
begin
Result := ...;
end;
procedure TForm5.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
URI, Content: String;
begin
URI := ARequestInfo.URI;
TThread.Synchronize(nil,
procedure
begin
Content := ProcessIt(URI);
end
);
AResponseInfo.ContentText := Content;
end;
Alternatively:
function TForm5.ProcessIt(const URI: string): String;
begin
Result := ...;
end;
procedure TForm5.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
TThread.Synchronize(nil,
procedure
begin
AResponseInfo.ContentText := ProcessIt(ARequestInfo.URI);
end
);
end;