embedio
embedio copied to clipboard
EmbedIO HttpListenerMode doesn't detect client disconnects
A quick overview: A number of client apps connect via a rev proxy to a service. The rev proxy is an ASP.NET app and the service is a c# windows service using EmbedIO to handle incoming requests. Client apps have two connections each: one for pushing updates and the one polling for changes. The issue described here is related to the polling.
The Bug: When using Microsoft HttpListenerMode when a client disconnects & the rev proxy removes the connection in response, the next write on the poll connection will fail (i.e. the service will fail to send any updates or heartbeat to the client via the rev proxy).
When using EmbedIO HttpListenerMode in the exact same scenario, the service writes keep succeeding with no errors reported at all. Even if the rev proxy process is terminated (i.e. stopping the IIS server hosting the rev proxy) the writes keep working with no exceptions thrown.
That feels like a bug to me.
Hello @ketodiet, thanks for using EmbedIO!
The behavior you describe feels like a bug to me too.
Which type of module do you use for the polling? Looks like WebSocketModule
from what you write, but I'd like to be sure.
Hi Riccardo, I'm not using websockets. The WebServer setup is:
_PublicAPI = new WebServer(o => o .WithUrlPrefix($"http://*:{port}/") .WithMode(HttpListenerMode.Microsoft) .WithSupportCompressedRequests(true)) .WithModule(new ActionModule(ctx => APIs.PublicAPI.Handle(ctx)));
Changing .WithMode
from HttpListenerMode.Microsoft
to HttpListenerMode.EmbedIO
changes the behaviour as described in the bug.
In the case of HttpListenerMode.Microsoft
I always get an System.Net.HttpListenerException
exception when attempting to write on a closed connection.
Hi @ketodiet, sorry for the delay. From what I could see from the source code, there are actually some flaws in connection management. I've begun an almost complete refactor of that part of EmbedIO, but in doing so I had to introduce a handful of breaking changes, so I'm afraid you'll have to wait for version 4 to see this issue (hopefully) fixed.
Hi Riccardo,
We're experiencing the exact same issue. Do you have any idea when we may see v4?
@ketodiet, @simontuffley can you give me an example of code for continuous writes on HTTP connection?
I confirm that there is such disconnection behavior. the code to repeat the error is simple:
[Route(HttpVerbs.Get, "/test")]
public async Task Test()
{
try {
byte[] dataBuffer = new byte[512];
using (var stream = HttpContext.OpenResponseStream()) {
while (true) {
await stream.WriteAsync(dataBuffer, 0, dataBuffer.Length);
await Task.Delay(1000);
}
}
} catch (Exception e) { Debug.WriteLine($"{e}"); }
}
It is convenient to receive data using curl command line, as well as terminate connections using ctl-c.
At the same time, in the debugger during debugging, the occurring exceptions associated with the advice are visible, but they are not returned as exceptions to the code of the method from which it was called.
Small changes in the source codes of the library easily solve this problem.
The file /src/packages/EmbedIO/Net/Internal/ResponseStream
needs to be modified:
remove:
internal class ResponseStream : Stream
insert:
public class ResponseStream : Stream
remove:
private readonly bool _ignoreErrors;
insert and rename all _ignoreErrors
to IgnoreErrors
public bool IgnoreErrors { get; set; } = true;
next:
[Route(HttpVerbs.Get, "/test")]
public async Task Test()
{
try {
byte[] dataBuffer = new byte[512];
using (var stream = HttpContext.OpenResponseStream()) {
if (stream is EmbedIO.Net.Internal.ResponseStream s)
s.IgnoreErrors = false;
while (true) {
await stream.WriteAsync(dataBuffer, 0, dataBuffer.Length);
await Task.Delay(1000);
}
}
} catch (Exception e) { Debug.WriteLine($"{e}"); }
}
Another solution to this issue is globally enabling the write errors by configuring the WebServer
's Listener
object:
server.Listener.IgnoreWriteExceptions = false;
await server.RunAsync();
This method does not require touching the library internal class.