DataSnap REST Server Client's timeout issue - Berlin - delphi

I am in some strange situation. I made DataSnap REST server and client. All REST server's methods are call by client through TRESTClient. My REST Server is Apache Module. Also I used TSQLConnection & TDSClientCallbackChannelManager for Peer-to-Peer callback in cleint. I set TDSServer ChannelResponseTimeout = 0 and TDSHTTPWebDispatcher SessionTimeout = 0. Still my client timed out after few seconds. I set TDSClientCallbackChannelManager CommunicationTimeout=0 and ConnectionTimeout=0. The error I am getting in TWinHTTPClient.DoExecuteRequest method of System.Net.HttpClient.Win. Strange thing is on debug mode I got AV but in exe mode I don't receive any AV but none of my callback is working though the REST methods are executing. I also tried set LifeCyle of TDSServerClass to Session & Invocation, both gives timed out. Below is some code for DSClientCallback & TSQLConnection:
SQLConnection.Params.Values['HostName'] := SERVERIP;
SQLConnection.Params.Values['Port'] := SERVER_PORT.ToString;
SQLConnection.Params.Values['ConnectionTimeout'] := '0';
SQLConnection.Connected := True;
ClientCallbackManager.CommunicationTimeout := '0';
ClientCallbackManager.ConnectionTimeout := '0';
ClientCallbackManager.DSHostname := SERVERIP;
ClientCallbackManager.DSPort := SERVER_PORT.ToString;
fClientCallbackId := TDSTunnelSession.GenerateSessionId;
ClientCallbackManager.DSPath := 'mypath';
ClientCallbackManager.ManagerId := TDSTunnelSession.GenerateSessionId;
fClientId := ClientCallbackManager.ManagerId;
ClientCallbackManager.RegisterCallback(fClientCallbackId,
'mychannel', TServerCallback.Create);
What I am doing wrong or missing? Please help. I also post this to Embarcadero Datasnap Forum without any response https://forums.embarcadero.com/thread.jspa?threadID=229678&tstart=0

Related

TIdHTTP.Get timeouts while the same call done with Postman succeeds: possible reasons?

I call a webapi with a Delphi app, in some pcs, the call timeouts, while in other it works fine.
The request done with Postman works fine.
It is a simple custom ping webservice (URL is in Edit1.Text in the code below), in fact the answer is a textual "Pong".
This is the Delphi code of the call:
errormsg := '';
{
old way of setting custom headers
IdHTTP1.Request.CustomHeaders.AddValue('X-HTTP-Method-Override', 'ForwardCommand');
IdHTTP1.Request.CustomHeaders.AddValue('Connection', 'keep-alive');
IdHTTP1.Request.CustomHeaders.AddValue('Accept', '*/*');
IdHTTP1.Request.CustomHeaders.AddValue('User-Agent', 'QualibusSilent');
IdHTTP1.Request.CustomHeaders.AddValue('Content-Type', 'text/plain');
}
//better way of setting custom headers
IdHTTP1.Request.MethodOverride := 'ForwardCommand';
IdHTTP1.Request.Connection := 'keep-alive';
IdHTTP1.Request.UserAgent := 'myCustomUserAgent';
IdHTTP1.Request.ContentType := 'text/plain';
IdHTTP1.Request.Accept := '*/*';
IdSSLIOHandlerSocketOpenSSL1 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode := sslmClient;
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.SSLVersions:=
[sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2];
startTime := GetTickCount;
Try
sHTML := IdHTTP1.Get(Edit1.Text);
Except
On E:Exception do
errormsg := e.Message;
End;
EndTime := GetTickCount;
ShowMessage('Time taken: ' +
IntToStr(endTime-startTime)+#13#10+'Error:'+errormsg);
Basically it is a call where instead of GET I do a custom method (ForwardCommand) that I call with X-HTTP-Method-Override.
In the code above I tried to add many headers so that the call is really as the Postman one.
If the call is done directly to the IP address it works, but if I call the https URL it timeouts, there is no evidence of error in the proxy server.
Checking the logs at the webserver side it seems the call is not performed at all.
And this occurs only from some Windows 10 machines, while in the majority of them the call is performed correctly.
Could you please suggest which could be the cause of the error? What should I try to change in the Delphi code to avoid the timeout so that Delphi behaves like Postman?
Thanks.
As stated in comments:
Why when Tidhttp uses proxyParams timeout does not occur and the call succeeds?
...
I finally got the reason for the Postman vs Indy behavior: Proxy. By passing proxy IP and port to TIdHTTP it works, Postman manages to retrieve the system proxy automatically and therefore it works.
There is no "system proxy" on Windows, however there is a proxy in the WinInet API, which is what Internet Explorer (and Edge?) relies on.
In any case, it sounds like the failing PCs don't have direct access to the Internet to begin with, only through a proxy. Indy has no concept of any "system proxy" on any platform, so you will have to assign the proxy settings to TIdHTTP manually, as you have discovered.

Indy IdFTP fails on Active connection

I'm trying to use Indy's IdFTP to send and receive some files via FTP.
function TDatosFTP.TransfiereFTP(Fm: TForm): boolean;
var
TimeoutFTP: integer;
begin
Result := false;
with TIdFTP.Create(Fm) do
try
try
TimeoutFTP := 2000;
Host := Servidor;
Port := 21;
UserName := Usuario;
PassWord := Contra;
Passive := Pasivo;
Connect(true, TimeoutFTP);
if not Connected then
begin
Error := true;
end
else
begin
TransferType := ftASCII;
if Binario then
TransferType := ftBinary;
OnWorkEnd := FinDeTransmision;
if Descargar then
Get(Remoto , Local, True)
else
Put(InterpretarRutaEspecial(Local), Remoto, True);
if Descargar and Borrar then
Delete(Remoto);
Disconnect;
Result := true;
Fm.Hide;
end;
Except on E: Exception do
Mensaje := E.Message;
end;
finally
Free;
end;
if not Result then
ErrorTransmision;
end;
Whenever I try to do a PUT/GET on Active mode I get the following error: EIdProtocolReplyError: 'Failed to establish connection". It works fine on Passive mode.
The thing is that I want to use Indy (used elsewhere in the project) but the previous version of the code, using OverbyteIcsFtpCli works fine both in Active and Passive mode.
This is the code using OverbyteIcsFtpCli:
function TDatosFTP.TransfiereFTP(Fm: TForm): boolean;
begin
with TFtpClient.Create(Fm) do
try
HostName := Servidor;
Port := '21';
UserName := Usuario;
PassWord := Contra;
HostDirName := '';
HostFileName := Origen;
LocalFileName := InterpretarRutaEspecial(Destino);
Binary := Binario;
Passive := Pasivo;
OnRequestDone := FinDeTransmision;
if Descargar then
Result := Receive
else
Result := Transmit;
OnRequestDone := nil;
if Descargar and Borrar then
Delete;
Result := Result and not Error;
Fm.Hide;
if not Result then
ErrorTransmision;
finally
Free;
end;
end;
So I took a look under the hood using wireshark and I found that Indy's FTP is not answering some messages from the server.
This is the file-transmission handshake with OverBytes' FTP:
I've highlighted in yellow the two packets sent between server and client that start the data transmission.
Now let's see what happens with Indy's FTP:
The server is sending the packet to start the file transmission but IdFTP is not answering.
I've seen this question but this two tests where ran in the same computer, same network connection, same firewall, etc. Also this one, but I want the FTP to work both in active and passive modes.
What's happening?
In an Active mode transfer, an FTP server creates an outgoing TCP connection to the receiver.
Your Wireshark captures clearly show that the FTP server in question is creating that transfer connection BEFORE sending a response to the RETR command to let your client know that the connection is proceeding. TFtpClient is accepting that connection before receiving the RETR response. But TIdFTP waits for the RETR response before it will then accept the transfer connection (this also applies to TIdFTP's handling of STOR/STOU/APPE commands, too).
LPortSv.BeginListen; // <-- opens a listening port for transfer
...
SendPort(LPortSv.Binding); // <-- sends the PORT command
...
SendCmd(ACommand, [125, 150, 154]); // <-- sends the RETR command and waits for a response!
...
LPortSv.Listen(ListenTimeout); // <-- accepts the transfer connection
...
Re-reading RFC 959, it says the following:
The passive data transfer process (this may be a user-DTP or a second server-DTP) shall "listen" on the data port prior to sending a transfer request command. The FTP request command determines the direction of the data transfer. The server, upon receiving the transfer request, will initiate the data connection to the port. When the connection is established, the data transfer begins between DTP's, and the server-PI sends a confirming reply to the user-PI.
ICS is asynchronous, so this situation is not a big deal for it to handle. But Indy uses blocking sockets, so TIdFTP will need to be updated to account for this situation, likely by monitoring both command and transfer ports simultaneously so it can act accordingly regardless of the order in which the transfer connection and the command response arrive in.
I have opened a ticket in Indy's issue tracker for this:
#300: TIdFTP fails on Active mode transfer connection with vsFTPd
UPDATE: the fix has been merged into the main code now.

How to resolve Indy DoMaxConnectionsExceeded

I have created a webserver using Delphi2007 and Indy10.
The server runs fine initially, but after time (usually between 8 and 48 hours) the DoMaxConnectionsExceeded method begins to fire; and my web server no longer works properly. Currently I have MaxConnections set to 500. I have experimented in the past with changing this setting. And it does seem the larger the value the longer the web server will live. So it makes me think connections are not being released.
Am I doing something wrong in my instantiation? Why are connections not being released?
Is there a way to get a list of all connections (by doing this I can check ip addresses and see if it might be DOS attack). Also a way to get the url they are trying to hit?
I have also experimented with the KeepAlive property with no change.
Should I set MaxConnections to 0?
Source Code for instantiating the TIdHTTPServer:
IdHTTPServer1 := TIdHTTPServer.Create;
IdHTTPServer1.MaxConnections := 500;
IdHTTPServer1.AutoStartSession := True;
IdHTTPServer1.KeepAlive := FGlobalKeepAlive;
IdHTTPServer1.SessionState := True;
IdHTTPServer1.OnCommandGet := IdHTTPServer1CommandGet;
IdHTTPServer1.onexception := IdHttpServerexception;
IdHTTPServer1.onlistenexception := IdHttpServerlistenexception;
idHttpServer1.ParseParams := True;
idHttpServer1.OnQuerySSLPort := QuerySSLPort;
idHttpServer1.IOHandler := ServerIOHandler;
idHttpServer1.Bindings.Add.Port := 80;
idHttpServer1.Bindings.Add.Port := 443;
IdHTTPServer1.Active := True;
Update - want to add I suspect it might be related to SSL. I have similar Indy based web servers that don't need SSL. While they do periodically fail, they don't fail nearly as often. But with these I am not logging DoMaxConnectionsExceeded. I will add tracking of this event to see if and when they do fail it is because maxconnections is exceeded.
Found out what was wrong. While I had set KeepAlive to false on the idHTTPServer component, in another area I was manually setting it on the ResponseInfo:
Aresponseinfo.Connection:='keep-alive';

Overbyte ICS NOFORMS mode

I'm trying to use Overbyte ICS TWsocket in a Delphi console application (service).
I have set NOFORMS in a conditionals.
But at Connect the connection doesn't go to the wsConnected it hangs in a wsConnecting state and the operation finishes with Async socket error 10053.
I've tried to use in a OnMessagePump the ProcessMessages(), MessageLoop() but it didn't change anything.
Here is a part of a code
inherited Create(nil);
Self.OnDataAvailable := MyOnReceive;
Self.OnChangeState := MyOnStateChange;
Self.OnMessagePump := MyMessagePump;
Addr := AIpAddress;
Port := IntToStr(AConnectionInfo.Port);
Proto := AConnectionInfo.Proto;
ComponentOptions := [wsoTcpNoDelay];
How to use TWsocket in a console application with NOFORMS correctly?

Indy idHttp freezes - How to work with keep-alive?

I developed a Webserver that uses idHttpServer and a client application that uses idHTTP.
I am using Delphi 2010 and the latest indy svn source from trunk.
This application sends about 1000 requests to the Web Server in a loop. Because of TIME_WAITS and the overhead of connecting to a webserver, I need to use keep-alive. The problem is: after making about 700 requests to the server, my application (the client side) hangs for almost 10 minutes when posting data to the webserver (that happens almost every time).
So, I need to know how to properly use keep-alive with indy.
So far I have this code:
On the client side:
oIndyHttpClient := TIdHTTP.Create(nil);
oIndyHttpClient.ProxyParams.Clear;
oIndyHttpClient.Request.CacheControl := 'no-cache';
oIndyHttpClient.ProtocolVersion := pv1_1;
oIndyHttpClient.HTTPOptions := oIndyHttpClient.HTTPOptions + [hoKeepOrigProtocol];
oIndyHttpClient.ReuseSocket := rsOSDependent;
oIndyHttpClient.Request.Connection := 'keep-alive';
And on the server side:
oIdHttpServer.OnCommandGet := Self.OnClientRead;
oIdHttpServer.AutoStartSession := False;
oIdHttpServer.KeepAlive := False;
procedure TPLKWSServerSocketIndy.OnClientRead(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
//do some stuff here
if LowerCase(ARequestInfo.Connection) = 'keep-alive' then begin
AResponseInfo.CloseConnection := False;
end
else begin
AResponseInfo.CloseConnection := True;
end;
end;
Am i doing it right? What can be causing the client application do freeze and do not complete the post request?
I tried do debug the server when the client freezes, but the OnClientReadmethod does not get fired. It seems to me that the client is having issues trying to connect do the web server.
If I modify the client code to:
oIndyHttpClient.ProtocolVersion := pv1_0;
oIndyHttpClient.Request.Connection := 'close';
The client app does not freeze and everything works nice.
Should I clear IOHandler.InputBuffer before sending a request to the server? Is there anything else I need to do?
Thanks
You do not need to manage keep-alives manually on the server side. TIdHTTPServer handles that for you. Simply set the TIdHTTPServer.KeepAlive property to True (it is False by default, and your code is setting it to False anyway) and do not set the AResponseInfo.CloseConnection property at all. TIdHTTPServer decides what value to set it to, on a per-request basis, before firing the OnCommandGet event.

Resources