Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to terminate HTTP Thread after WebSocket upgrade
#1
I have a HTTP Server that performs the upgrade to a WebSocket server after serving the client with the html file containing the websocket client code on connect. Everything is working fine except the original TCP Thread is hanging in there as a ghost thread after the websocket upgrade, that is using a new thread.

I am looking for a possible way on how to terminate that thread. I can post the relevant snippet where the upgrade is happening, if required.

This is based on TIdCustomTCPServer and staspiter Websocketserver.

Thank you for any fixes or pointers.
Reply
#2
(08-01-2026, 11:27 AM)hamstring Wrote: Everything is working fine except the original TCP Thread is hanging in there as a ghost thread after the websocket upgrade, that is using a new thread.

I am not familiar with staspiter Websocketserver, but is there a reason why you are moving the websocket to a new thread and not managing it on the original thread? The original thread is intended to continue running until its socket connection is closed. HTTP can receive multiple requests on a single connection, but once you upgrade a connection to websocket then it can't handle anything else until it is closed. Since you have to leave the connection open to service your websocket, you may as well just keep using the original thread for that purpose.

(08-01-2026, 11:27 AM)hamstring Wrote: I am looking for a possible way on how to terminate that thread.

Indy will handle that internally once the socket connection has been closed and control flow has returned to TIdCustomTCPServer. Do whatever you need to do inside of your event handler that processed the upgrade request while the connection is still alive.

But, if for whatever reason, you really need to transfer the socket to another thread, then you will have to release Indy's ownership over the socket (which will terminate the original thread once flow returns to TIdCustomTCPServer). You can use the TIdContext.Binding.Handle property for that. Read the property to get the socket, and then set the property to Id_INVALID_SOCKET to release ownership of the socket (setting the property will not close the socket, so you will have to close it manually when you are done using it).

Reply
#3
It has always been my understanding that the original thread would be retained after the upgrade and re-used for the handling of the websocket communications as well. The original basic code of the WebsocketServer required that the client has the websocket program installed on clients device before connecting. Now with a HTTP add-on the client is getting served the websocket html document at connect time, followed by the upgrade request.

I don't know why a new thread is created after the upgrade, perhaps the initial HTTP thread creation is not aware that it has to handle the websocket following the upgrade. My big problem is that I am new to Indy and lacking the knowledge of what is happening at backstage.

What is the forum policy for attachments, the file size is 11 KB and I don't really want to post it here. Remy would you be willing to have a look at it as a PM, I'm sure it would be trivial for you to find the problem?
Reply
#4
(08-02-2026, 02:17 AM)hamstring Wrote: It has always been my understanding that the original thread would be retained after the upgrade and re-used for the handling of the websocket communications as well.

Only if you code your event handler to work that way. TIdCustomTCPServer doesn't care what you do with the socket. Its Execute event just loops until the connection is closed. You define what the event does on each iteration. If you were using TId(Custom)HTTPServer, its Execute logic reads an HTTP request and sends an HTTP response per iteration. You would service the websocket in the event handler after sending the upgrade response, so flow doesn't return to the server for the next request.

(08-02-2026, 02:17 AM)hamstring Wrote: I don't know why a new thread is created after the upgrade


What makes you think a new thread is being created? Where are you seeing it, exactly?

It's possible that the client is using one connection+thread to retrieve the HTML page, and then another connection+thread to connect the websocket. An upgrade starts as an HTTP request, and there is no requirement in HTTP that multiple requests use the same connection. Is your HTTP client/server using keep-alives between requests?

By default, Indy uses 1 thread per connection. You can opt-in to pool threads for reuse across connections.

HTTP can use multiple connections across requests, but Websocket does not. Once an HTTP connection is upgraded to Websocket, the connection is used only for that Websocket until the connection is closed.

(08-02-2026, 02:17 AM)hamstring Wrote: perhaps the initial HTTP thread creation is not aware that it has to handle the websocket following the upgrade.

Indy has no concept of websockets. That is just extra logic you build into your HTTP request handler.

(08-02-2026, 02:17 AM)hamstring Wrote: My big problem is that I am new to Indy and lacking the knowledge of what is happening at backstage.


It would be useful if you could show the actual code you are struggling with. Don't need the whole project, just the HTTP/upgrade portion.

Reply
#5
I think that the reason for getting two threads created might be that the http connection and websocket context were not separated.
Remy supplied a change of code (addition) in the TWebSocketServer.Create constructor:

FWebSocketServer.OnWebSocketExecute := DoServerWebSocketExecute;

Whereas before I had been handling everything in the DoServerExecute procedure.

FWebSocketServer.OnWebSocketExecute := Self.DoServerExecute;

FWebSocketServer.OnExecute := DoServerExecute;
FWebSocketServer.OnConnect := DoServerConnect;
FWebSocketServer.OnDisconnect := DoServerDisconnect;

Now trying suggested code change:

Code:
constructor TMyWebSocketManager.Create;
begin
inherited Create(nil);
FWebSocketServer := TWebSocketServer.Create;

// Initialize the thread lock container
FSessionLock := TCriticalSection.Create;

// Initialize the dictionary container to hold your session memory references
FSessionMap := TDictionary<string, TMyClientSession>.Create;

FWebSocketServer.ContextClass := TMyClientContext;

FWebSocketServer.OnWebSocketExecute := DoServerWebSocketExecute;

FWebSocketServer.OnConnect := DoServerConnect;
FWebSocketServer.OnDisconnect := DoServerDisconnect;

FCommandQueue := TThreadedQueue<TWSCommand>.Create(1024, 0, 0);

LogAddMsg('Server created, waiting for activation');
end;

My question now is whether FWebSocketServer.OnExecute := DoServerExecute; is not required anymore or what should it be used for? What is the difference between FWebSocketServer.OnExecute and FWebSocketServer.OnWebSocketExecute ?
Reply
#6
(08-13-2026, 05:14 AM)hamstring Wrote: I think that the reason for getting two threads created might be that the http connection and websocket context were not separated.

I didn't see all of your code, but my guess is you are getting 2 threads for 2 separate TCP connections - one for the HTTP client that receives the HTML, and one for the WebSocket.

(08-13-2026, 05:14 AM)hamstring Wrote: My question now is whether FWebSocketServer.OnExecute := DoServerExecute; is not required anymore or what should it be used for?

Your DoServerExecute() is useless and should be removed. TWebSocketServer overrides DoExecute() and does not call inherited, so the OnExecute event is never triggered.

(08-13-2026, 05:14 AM)hamstring Wrote: What is the difference between FWebSocketServer.OnExecute and FWebSocketServer.OnWebSocketExecute ?

OnExecute is triggered by the inherited DoExecute() on each iteration of the thread's loop. It is meant to handle any processing needed for the TCP connection as a whole for the current iteration. But since DoExecute() is overriden, this doesn't happen.

OnWebSocketExecute is triggered by the overriden DoExecute() on each iteration, after the WebSocket handshake has been completed. It is meant to handle any processing needed for just the WebSocket on the current iteration.

Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)