(04-18-2022, 04:13 PM)rlebeau Wrote: Why are you waiting for the OnExecute event? You should disconnect them in the OnConnect event if the WebSocket handshake is not valid.
I originally performed the disconnects in OnConnect but I saw a post from you in StackOverFlow telling someone else to do it in OnExecute. Perhaps I misunderstood the context. Any in case, I get the same results either way.
Quote:What is the point of doing that? First off, Indy threads are already named by Indy.
Just for debugging purposes to see how far the thread progresses. And to tie them to IP addresses to see if they are rogue or from players on the game server.
Quote:But more importantly, having MadExcept name threads only makes sense if an error report is generated, and you shouldn't be getting an error just for disconnecting clients normally.
It's too late to name them after an error occurs. Like I mentioned, I have a report that generates a list of all running threads to help debug the very type of situation I'm dealing with now: zombie threads using up system resources.
Quote:Do you have thread pooling enabled on your server?
I don't know what that is so probably not unless it's on by default.
Quote:What do AdminSessionDisconnect() and PlayerSessionDisconnect() look like?
I've not had any issues with normal connections terminating. None of those leave zombie threads behind. But here are those OnDisconnect events:
Code:
procedure AdminSessionDisconnect(AContext: TIdContext);
var
ID, IP, User: string;
Admin: TAdminSession;
begin
ID := TConnectRec(AContext.Data).ID;
IP := '?';
User := '?';
Admin := TAdminSession(AdminSessionList.GetKeyObject(ID));
if Admin <> Nil then
begin
IP := Admin.IP;
User := Admin.UserName;
if Admin.ReleaseAndFree then FreeAndNil(Admin);
end;
AdminSessionList.RemoveKey(ID);
AdminSessionUpdate.QueueAdd('Del', ID);
LogData.AddEvent('Admin', User + ' logged out session ' + ID + ' from IP ' + IP);
end;
procedure PlayerSessionDisconnect(AContext: TIdContext);
var
PS: TPlayerSession;
ID, p: string;
begin
if gvar.Running = False then Exit;
if Assigned(AContext.Data) then ID := TConnectRec(AContext.Data).ID else ID := '';
if ID = '' then Exit;
PS := TPlayerSession(PlayerSessionList.GetKeyObject(ID));
if PlayerSessionList.OpenObject(PS) then // make sure session object not already destroyed
begin
PS.ReleaseAndFree; // decrement AccessCount from GetKeyObject
PS.PacketCS.Enter;
try
PS.Status := 'Discon';
PS.Disconnected := Now;
PS.Connection := Nil;
SessionUpdate.QueueAdd('Ref', PS.ID);
p := PS.Player;
if p = '' then p := 'IP ' + PS.IP;
LogData.AddEvent('Connect', p + ' disconnects session ' + PS.ID + ', PC ' + PS.PC);
finally
PS.PacketCS.Leave;
if PS.ReleaseAndFree then FreeAndNil(PS);
end;
end;
end;
FYI, this was the StackOverFlow thread I was referring to:
https://stackoverflow.com/questions/4500...the-server
And if helps, here is my OnConnect event:
Code:
class procedure WSServerEvents.OnConnect(AContext: TIdContext);
var
IO: TIdIOHandler;
Key, ProxyHeader, Header, LCHeader, RawHost, Host, RawOrigin, Origin, IP, ProxyIP: string;
Admin: boolean;
c: integer;
begin
try
IP := AContext.Binding.PeerIP;
MadExcept.NameThread(GetCurrentThreadID, 'WebSocket Connect (' + IP + ')');
AContext.Connection.Intercept := TIdConnectionIntercept.Create(AContext.Connection); // track total bytes in and out
AContext.Connection.Intercept.OnReceive := InterceptEvents.OnReceive;
AContext.Connection.Intercept.OnSend := InterceptEvents.OnSend;
if (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough:= false;
IO := AContext.Connection.IOHandler;
IO.ReadTimeout := 10000; // 10 second readln timeout
ProxyIP := IP;
Key := '';
ProxyHeader := LowerCase(Trim(sysProxyIPHeader.sValue));
RawHost := '';
RawOrigin := '';
Host := '';
Origin := '';
IP := '';
Admin := False;
c := 0;
MadExcept.NameThread(GetCurrentThreadID, 'WebSocket Header Start (' + ProxyIP + ')');
repeat // read headers until a blank line received
Sleep(10);
Header := IO.ReadLn();
LCHeader := LowerCase(Header);
if Pos('get /admin', LCHeader) = 1 then Admin := True else
if Pos('host:', LCHeader) = 1 then
begin
RawHost := Trim(Copy(Header, 6, 255));
Host := URLToHost('http://' + RawHost);
end else
if Pos('origin:', LCHeader) = 1 then
begin
RawOrigin := Trim(Copy(Header, 8, 255));
Origin := URLToHost(RawOrigin);
end else
if Pos('sec-websocket-key:', LCHeader) = 1 then Key := Trim(Copy(Header, 19, 99))
else if (ProxyHeader <> '') and (Pos(ProxyHeader + ':', LCHeader) = 1) then
IP := Trim(Copy(Header, Length(ProxyHeader) + 2, 99));
Inc(c);
until (Header = '') or (c > 50); // 50 header lines max
MadExcept.NameThread(GetCurrentThreadID, 'WebSocket Header End (' + ProxyIP + ')');
if Key = '' then Exit; // invalid websocket header, disconnect in OnExecute
if not(Admin) and not(gvar.Running) then Exit; // offline for players, disconnect in OnExecute
MadExcept.NameThread(GetCurrentThreadID, 'WebSocket Key Ok (' + ProxyIP + ')');
// send response headers and hashed key in accordance with RFC 6455
IO.WriteLn('HTTP/1.1 101 Switching Protocols');
IO.WriteLn('Upgrade: websocket');
IO.WriteLn('Connection: Upgrade');
IO.WriteLn('Sec-WebSocket-Accept: ' + HashKey(Key));
IO.WriteLn('');
if sysSameOriginPolicy.bValue and (Host <> Origin) then
begin
SendCloseFrame(AContext, 4000);
Sleep(1000); // enough time for writes to go out
Exit; // disconnect in OnExecute
end;
if IP = '' then
begin
IP := ProxyIP;
ProxyIP := '';
end;
if Admin then AdminSessionConnect(AContext, IP, ProxyIP)
else PlayerSessionConnect(AContext, IP, ProxyIP);
except
on E: Exception do
begin
if E is EIdException then raise else
begin
LogData.AddError('WSServerConnect error: ' + E.Message);
raise;
end;
end;
end;
end;