This is a heck of a wall to hit at the end of a 1.5 week refactoring.
I've gotten it down to the bear minimum and I'm at an absolute loss
I start a SignalR webserver using owin selfhost (katana), connect to it. I shut it down, and then I start it and try to connect to it again.
On the second time through (it works just fine the first time) I get an error while trying to initiate a connection:
public partial class App : Application
{
void App_Startup(object sender, StartupEventArgs e)
{
using (var server = new TestWebServer()) {
server.Start();
using (var hubConnection = new HubConnection(TestWebServer.Host)) {
var proxy = hubConnection.CreateHubProxy("testHub");
hubConnection.Start().Wait();
Debug.Assert(hubConnection.State == ConnectionState.Connected);
}
}
// Makes it here fine
using (var server = new TestWebServer()) {
server.Start();
using (var hubConnection = new HubConnection(TestWebServer.Host)) {
var proxy = hubConnection.CreateHubProxy("testHub");
hubConnection.Start().Wait(); //<-throws "Transport timed out trying to connect"
Debug.Assert(hubConnection.State == ConnectionState.Connected);
}
}
}
}
With TestWebServer
:
public class TestWebServer : IDisposable {
public const string Host = "http://localhost:8081/";
public void Start() {
if (null != server) return;
this.server = WebApp.Start(Host, app => {
var cfg = new HubConfiguration { };
app.MapSignalR(cfg);
});
}
IDisposable server;
//Super-duper-for-real disposable https://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/
bool _disposed;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
~TestWebServer() {
Dispose(false);
}
protected virtual void Dispose(bool disposing) {
if (_disposed)
return;
if (disposing) {
if (null == server) return;
server.Dispose();
server = null;
}
_disposed = true;
}
}
And TestHub
[HubName("testHub")]
public class TestHub : Hub {
public void Ping() => Clients.All.pong();
}
This is using SignalR 2.2.0 and I've tried both on .Net 4.5 and 4.6.1
This is as simple a SignalR application as I possibly know how to make it and you notice it works! Once. Then it fails when I dispose and try the exact same code again.
What is going on? Is there something else that I need to do to dispose of things?
Yes, I've tried to Thread.Sleep
between the two.
Here is a tester project demonstrating the issue
All credit to my coworker Shamus who found this article and then was suspcious enough to hack at my demo project until the darn thing worked.
You can stop and restart if you give the HubConfiguration
a new instance of Resolver
every time. In other words check this nonsense out in the source.
In other, other words, do
var cfg = new HubConfiguration { Resolver = new DefaultDependencyResolver() };
For a good time that fixes everything.
SO what it seems like at the moment is that something that is stored in the dependency resolver is not being disposed of properly. (This suspicion is heightened by the fact that there is an exception that happens during the webserver disposal and is logged to Debug output). If you do not manually create your own resolver every time then...congratulations, you're just grabbing the same global resolver that contains improperly disposed objects.
So the answer seems to be that yes, this is a a SignalR bug, but also yes, there is a simple workaround.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With