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';
Related
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.
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
I've probably read dozens of answers and topics through the web, but I'm still missing something in order to fix this error. I have a TIdPop3Server and a TIdSMTPServer and I want to activate them, but I just can't do it successfully. I've set the ReuseSocket property of both to rsTrue and I'm not leaving the Bindings empty when I try to set them both to .Active := True; This is how my code looks like :
with POP3Server do begin
ReuseSocket := rsTrue;
Active := False;
Bindings.Clear;
DefaultPort := 110;
Bindings.Add.IP := myIpAddr;
end;
with SMTPServer do begin
ReuseSocket := rsTrue;
Active := False;
Bindings.Clear;
DefaultPort := 25;
Bindings.Add.IP := myIpAddr;
end;
And I have a TButton that I click where this is called :
SMTPServer.Active := True;
Pop3Server.Acive := True;
If someone had already fixed this problem can he tell me how he had done it (hope I didn't already read his answer somewhere else ...)
You did not say which server is failing to bind. But there is nothing to fix really. Something else on your machine, likely an antivirus or firewall, is already using one of those ports. Use a tool like Netstat or TCPViewer to find out which process is using those ports.
I have a small piece of code that checks if a computer is alive by pinging it. We use to have a room with 40 computer and I wanna check remotely through my program which on is alive.
Therefore I wrote a little ping function using indy
function TMainForm.Ping(const AHost : string) : Boolean;
var
MyIdIcmpClient : TIdIcmpClient;
begin
Result := True;
MyIdIcmpClient := TIdIcmpClient.Create(nil);
MyIdIcmpClient.ReceiveTimeout := 200;
MyIdIcmpClient.Host := AHost;
try
MyIdIcmpClient.Ping;
Application.ProcessMessages;
except
Result := False;
MyIdIcmpClient.Free;
Exit;
end;
if MyIdIcmpClient.ReplyStatus.ReplyStatusType <> rsEcho Then result := False;
MyIdIcmpClient.Free;
end;
So I've developped that at home on my wifi network and everthing just work fine.
When I get back to work I tested and I get an error saying
Socket Errod # 10040 Message too long
At work we have fixed IPs and all the computer and I are in the same subnet.
I tried to disconnect from the fixed IP and connect to the wifi which of course is DHCP and not in the same subnet, and it is just working fine.
I have tried searching the internet for this error and how to solve it but didn't find much info.
Of course I have tried to change the default buffer size to a larger value but it didn't change anything I still get the error on the fixed IP within same subnet.
Moreover, I don't know if this can help finding a solution, but my code treats exceptions, but in that case it takes about 3-4 seconds to raise the error whereas the Timeout is set to 200 milliseconds. And I cannot wait that long over each ping.
By the way I use delphi 2010 and I think it is indy 10. I also have tested on XE2 but same error.
Any idea
----- EDIT -----
This question is answered, now I try to have this running in multithread and I have asked another question for that
Delphi (XE2) Indy (10) Multithread Ping
Set the PacketSize property to 24:
function TMainForm.Ping(const AHost : string) : Boolean;
var
MyIdIcmpClient : TIdIcmpClient;
begin
Result := True;
MyIdIcmpClient := TIdIcmpClient.Create(self);
MyIdIcmpClient.ReceiveTimeout := 200;
MyIdIcmpClient.Host := AHost;
MyIdIcmpClient.PacketSize := 24;
MyIdIcmpClient.Protocol := 1;
MyIdIcmpClient.IPVersion := Id_IPv4;
try
MyIdIcmpClient.Ping;
// Application.ProcessMessages; // There's no need to call this!
except
Result := False;
Exit;
end;
if MyIdIcmpClient.ReplyStatus.ReplyStatusType <> rsEcho Then result := False;
MyIdIcmpClient.Free;
end;
For XE5 and Indy10 this is still a problem, even with different Packet Size.
To answer the more cryptical fix:
ABuffer := MyIdIcmpClient1.Host + StringOfChar(' ', 255);
This is a "magic" fix to get around the fact that there is a bug in the Indy10 component (if I have understood Remy Lebeau right).
My speculation is that this has some connection with the size of the receive buffer. To test my theory I can use any character and don't need to include the host address at all. Only use as many character you need for the receive buffer. I use this small code (C++ Builder XE5) to do a Ping with great success (all other values at their defaults):
AnsiString Proxy = StringOfChar('X',IcmpClient->PacketSize);
IcmpClient->Host = Host_Edit->Text;
IcmpClient->Ping(Proxy);
As you can see I create a string of the same length as the PacketSize property. What you fill it with is insignificant.
Maybe this can be of help to #RemyLebeau when he work on the fix.
use this code
ABuffer := MyIdIcmpClient1.Host + StringOfChar(' ', 255);
MyIdIcmpClient.Ping(ABuffer);
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.