Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Writing data directly to socket from TIdHttp to TIdHttpServer
#1
Hi,

I mentioned before in a previous thread that I am in the process of creating a Push notification service Client/Server. I am almost finished except for that I want to keep track of user status on client machines and update the service on the server side with this status. For example, if the user is logged in/out of our system, Busy with a task or not and if the PC is locked.

Now, I want to write directly from TIdHttp client to an TIdHttpServer but http servers in indy does not implement the OnExecute event handler like TCP servers do.

I don't want to get into the process of adding another TCP server on the push service because it will damage the structure of that type of server we have. It is similar to RAD server where the actual resource is inside a dll and the host app (server) only has 1 http server.

We can add a TCP server inside the dll resource itself but I don't want to have 2 connections from the same client just for the sake keeping track of user status. 

So, my questions are:

If I put a TCP server in the dll resource, is there a way to redirect the http connection from the client made with the http server (host app) to the TCP server inside the dll?

If I didn't use the TCP server inside the dll. Can expose the "OnExecute" event handler from TIdHttpServer? I want to be able to use TIdHttp.WriteRFC from the client side and read that on the server side. I don't want to use POST/PUT http requests for updating users status, I want this to use the minimum amount of data possible when send status pings to the push server.

I hope I demonstrated the full issue because I won't be able to share any code it is a very big project and also I am not allowed to.

Any help will be appreciated.
Thanks in advance
Reply
#2
(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: I want to keep track of user status on client machines and update the service on the server side with this status. For example, if the user is logged in/out of our system, Busy with a task or not and if the PC is locked.

OK, so why not simply have the HTTP client send its updated status to the HTTP server using a standard HTTP POST or PUT command?

(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: Now, I want to write directly from TIdHttp client to an TIdHttpServer

Why? That would likely cause you to end up violating the HTTP protocol. Besides, pushes are typically sent from the server to the client, not the other way around.

(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: but http servers in indy does not implement the OnExecute event handler like TCP servers do.

Because HTTP uses a command/response model, so TIdHTTPServer exposes events to handle commands sent by clients.

(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: We can add a TCP server inside the dll resource itself but I don't want to have 2 connections from the same client just for the sake keeping track of user status. 

HTTP is a stateless protocol, there is no guarantee that multiple commands from the same client will be able to reuse connections, even with HTTP keep-alives enabled. So, you have to be prepared to handle multiple connections, even from the same client.

(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: If I put a TCP server in the dll resource, is there a way to redirect the http connection from the client made with the http server (host app) to the TCP server inside the dll?

No.

(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: If I didn't use the TCP server inside the dll. Can expose the "OnExecute" event handler from TIdHttpServer?

Technically, yes, but doing so won't help you.

TIdHTTPServer derives from TIdCustomTCPServer, and the TIdCustomTCPServer.OnExecute event is protected. You would have to derive a new class from TIdHTTPServer to gain access to the event.

But even so, the event won't fire at runtime, because TIdHTTPServer overrides the virtual TIdCustomTCPServer.DoExecute() method to do all of its HTTP work, thus bypassing the OnExecute event.

(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: I want to be able to use TIdHttp.WriteRFC from the client side and read that on the server side.

First, TIdHTTP does not have a WriteRFC() method. Did you mean the WriteRFCStrings() method?

Second, using WriteRFCString() would absolutely violate the HTTP protocol. WriteRFCStrings() does not send data in a format that HTTP allows. You can't just send random arbitrary data at a connection and expect the receiver to handle it. HTTP is a protocol, it has structure and rules to it.

If you want to send custom data to an HTTP server, use the ASource input parameter of the TIdHTTP.Post() or TIdHTTP.Put() method.

(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: I don't want to use POST/PUT http requests for updating users status

Then why are you using the HTTP protocol at all?

(04-27-2022, 01:59 PM)Ahmed Sayed Wrote: I want this to use the minimum amount of data possible when send status pings to the push server.

Then use TIdTCPClient + TIdTCPServer instead of TIdHTTP + TIdHTTPServer. Invent your own protocol on top of TCP directly to suit your needs. You are approaching your issue all wrong.

(04-27-2022, 04:43 PM)rlebeau Wrote: OK, so why not simply have the HTTP client send its updated status to the HTTP server using a standard HTTP POST or PUT command?

That being said, there is one way you MIGHT be able to accomplish what you want using TIdHTTPServer, but you will not be able to accomplish it using TIdHTTP due to current design limitations in it, so you will have to use TIdTCPClient instead and implement the HTTP protocol manually on the client side.

Have the client send an HTTP 1.1 POST or PUT command, specifying Expect: 100-continue and Transfer-Encoding: chunked request headers. Do not send any body data yet to finish the request, just send the headers (TIdHTTP does not support this last part at this time).

When the server receives the request, it will detect the Expect header and send back an intermediate HTTP 1.1 100 Continue response, then wait for the remaining request body to arrive.

Have your client read that response, then start sending status updates to the socket connection as needed, in the 'chunked' format described in RFC 2616 Section 3.6.1 and RFC 7230 Section 4.1 (TIdHTTP does not currently support this, either). When there are no more updates to be sent, send a chunk with a size of 0, followed by empty trailer headers. Disconnect from the server if needed.

TIdHTTPServer will create a TIdHTTPResponseInfo.PostStream object and then enter a loop reading chunks, until it receives a chunk with a data size of 0, or the client disconnects. Each chunk will be written to the PostStream. However, by default, TIdHTTPServer will create a TMemoryStream for the PostStream, so you will have to use the TIdHTTPServer.OnCreatePostStream event to provide your own TStream instead. You can derive from TStream and override its virtual TStream.Write() method, or use Indy's TIdEventStream class with an OnWrite event handler assigned, to parse your status update chunks as needed.

After the last chunk has been received, TIdHTTPServer will fire the appropriate OnCommand... event for the original request. Simply do nothing, just exit.

With all of that said, this is a bit of an abuse of the HTTP protocol, but it will likely work for your particular use-case.

Reply
#3
Thanks for the help and quick response.

The reason I am using Http is that I am registering the client first for the connection with a POST request that will be sent once. That request will have only small data like user id and current status "Connected" or anything else indicating that the that specific client is online.

So, as I mentioned before I am forced to use HTTP because of the structure of this web server framework that we have. I didn't want to have another TCP server in the host app just for the sake of if ever I needed one in a similar case.

I am not trying to abuse any protocols here I am just trying to use the minimum resources as possible, in the worst case scenario I could put a TCP server inside the dll and connect to it from client. I just thought that there might be away to use the already existing server in the host app.
Reply
#4
(04-28-2022, 12:14 AM)Ahmed Sayed Wrote: I am not trying to abuse any protocols here I am just trying to use the minimum resources as possible

How often are you expecting a client to update its status? If it is pretty often, then I would just use standard HTTP keep-alives and POST requests. The overhead should be quite minimal. But, if the interval between updates is not very often, this will end up wasting more resources than is useful.

Reply
#5
The client will only send the status update when it has changed not on intervals. I guess a POST request every now and then wouldn't heart the app.
Reply
#6
I'm not trying to break any protocols; instead, I'm attempting to use as few resources as possible. In the worst-case situation, I could include a TCP server in the dll and connect to it from the client. I was simply thinking that there could be a way to use the host app's existing server.io games
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)