Many times in your life, you will have the situation when your server must push information to your customer. When you are not limited to HTTP/HTTPS you can setup TCP/IP connection and problems are solved. Unfortunately we are living in a world dominated by HTTP protocols. When you ask someone how to address such a challenge you can hear from your colleagues that Webhook (https://en.wikipedia.org/wiki/Webhook) is a solution for your problem. My goal is to convince you to try to think outside the box and focus on long pooling mechanisms.
Let’s start from the Webhook. Webhooks require a publicly exposed endpoint to make a call. Publicly exposed endpoints require static IP addresses or in case of dynamic IPs some kind of dynamic DNS involvement. Do not forget about opening the firewall to expose Webhook port. Not all customers can publicly expose the endpoint to consume your messages. When that is the case, you cannot use this solution at all because it is a technical blocker.
SignalR for a rescue
In the .NET world you can hear that SignalR (https://en.wikipedia.org/wiki/SignalR) should address all your needs. SignalR does not have such a problem like static IP for clients’ endpoints, but it can be too heavy for our needs. The main disadvantage I can see in SignalR is that it does not support reliable messaging. When connection is lost you can lose messages. So you must implement your own reliability layer. The question is – if you need to add this future, why do not implement a solution from scratch.
I think a lot of software developers are not aware that HTTP response does not have to be a short lived process. You can easily send a request and wait really long for a response. By applying a few tricks your connection can live `forever`. Additionally, a response does not have to represent a single message. It can represent a stream of messages sent one by one in chunks.
A good example is a project called NDJSON (https://github.com/ndjson/ndjson-spec) which addresses independent messages as a stream of JSON objects separated by a new line each. If we are able to create a server sending messages in those chunks we can call a day.
Last two missing spices to cook a solution
ASP .NET by default can wait on a task, but expects a single response when your data is ready. Fortunately we are not lost here. To build your own Long Polling solution in .NET you need to add two spices to your recipe. The first spice is creating the controller method with IAsyncEnumerable interface as a response. You need additionally to implement custom OutputFormatter – to return HTTP responses in NDJSON compatible format. The second missing spice is TaskCompletionSource allowing you to wait for incoming changes.
On my GitHub you can find a project https://github.com/MariuszKrzanowski/demo-long-polling where the long polling solution is presented. The main server side has less than 100 lines of C# code allowing you to implement your own solution in future. It is a simple solution and requires a lot of tests, but can be an inspiration for you. My goal was to give you a starting point for your own solution. I propose to start analysis from https://github.com/MariuszKrzanowski/demo-long-polling/blob/main/src/Server/Controllers/EventsController.cs file where main logic is implemented.