I have created a TIdHTTPServer based web server that runs on Windows. Indy version is very recent (Jan 2015). I have MaxConnections set to 1000, and have developed a mechanism to restart the web server when DoMaxConnections is called.
Once an hour I log the connection count:
TIdThreadSafeList(IdHTTPServer1.Contexts).count;
For the most part the connection count is always increasing. Once in a while I will see it decrease (say from 525 to 520). But the overall trend is increasing. After 5 days or so it gets to 1000 and the server resets (I understand 1000 is relatively small, but don't think it is germane to the subject).
Even on weekends, when traffic is lower, it still increases. I would have thought it would decrease at times of lower traffic. Why are some of these connections not closing?
I do netstat -an and see a lot of TIME_WAIT status.
Other relevant source code:
ServerIOHandler := TIdServerIOHandlerSSLOpenSSL.Create(self);
ServerIOHandler.SSLOptions.CertFile := 'C:\xxxxxx.crt';
ServerIOHandler.SSLOptions.KeyFile := 'C:\xxxxxx.key';
ServerIOHandler.SSLOptions.RootCertFile := 'C:=xxxxxx.crt';
ServerIOHandler.SSLOptions.Method := sslvSSLv23;
ServerIOHandler.SSLOptions.Mode := sslmServer;
IdHTTPServer1 := TIdHTTPServer.Create;
IdHTTPServer1.MaxConnections := 1000;
IdHTTPServer1.AutoStartSession := True;
IdHTTPServer1.KeepAlive := True;
IdHTTPServer1.SessionState := True;
IdHTTPServer1.OnCommandGet := MainGet;
idHttpServer1.ParseParams := True;
idHttpServer1.IOHandler := ServerIOHandler;
idHttpServer1.Bindings.Add.Port := 80;
idHttpServer1.Bindings.Add.Port := 443;
IdHTTPServer1.Active := True;
Update I thought it would be useful to add some netstat statistics.
With the server running for 24+ Hours:
Contexts Count: 587
netstat ESTABLISHED from external to port 80:580
netstat TIME_WAIT from external to port 80:819
I then restarted my Indy Service (did not reboot windows server however):
Contexts Count: 60
netstat ESTABLISHED from external to port 80:49
netstat TIME_WAIT from external to port 80:797
Update 2 - Request was made to show the MainGet procedure. There is too much code to feasibly do this. But what I have done is to provide snippets of code that could shed light on problem. Below is a list of some of the calls I am making (under different circumstances to improve performance).
AResponseInfo.ContentText := filetostring('c:\.....');
AResponseInfo.ResponseNo
Aresponseinfo.RawHeaders.Add('Connection:close');
Aresponseinfo.CustomHeaders.Add('Keep-Alive: timeout=30');
Aresponseinfo.Connection:='keep-alive';
AResponseInfo.ContentStream := MemoryStream;
AResponseInfo.Redirect('xxxxx');
AResponseInfo.ServeFile(AContext,mFile);
AResponseinfo.CacheControl:='max-age=1209600';
Update 3 - provide additional information of my the MainGet function. I am keeping track of the number of times the function is called and the number of times that it exits. The value of iGlobalGetStart and iGlobalGetFinish are always very close (certainly way less than number of contexts that are active).
procedure TMyWebUnit.MainGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
try
InterlockedIncrement(iGlobalGetStart);
.... all of my logic
finally
InterlockedIncrement(iGlobalGetFinish);
end;
Update 4 - I have implemented additional tracking code. I have created my own class descended from TIdServerContext. In it I set the date/time it is created, and also have a date/time member called DatetimeLastUsed which will get updated in oncommandget. Here is the class:
type
TEAIdServerContext = class(TIdServerContext)
DateTimelastUsed,DateTimeCreated: TDateTime;
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
destructor Destroy; override;
end;
I set the IdHTTPServer1.ContextClass to this new class: IdHTTPServer1.ContextClass := TEAIdServerContext;
In my OnCommandGet, the first thing I do is to set the date/time of DateTimeLastUsed to the current time:
TEAIDServerContext(AContext).DateTimeLastUsed := Now;
I also have a function which will list all contexts:
procedure SayContexts();
var
i: Integer;
mstring: String;
mList: TList;
begin
mList := IdHTTPServer1.Contexts.LockList;
mstring := 'Contexts:'+inttostr(mlist.Count);
with mList do try
for i := 0 to count -1 do begin
mString := mString +
datetimetostr(TEAIDServerContext(mList[i]).datetimecreated)+'; last used:'+datetimetostr(TEAIDServerContext(mList[i]).datetimeLastUsed)
end;
end;
IdHTTPServer1.Contexts.UnLockList;
....
end;
The connections that are remaining open are never getting to the oncommandget code because their datetimelastused field is null. Is this normal?