Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot shut down and start new SignalR using OWIN Self Host

Tags:

owin

signalr

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

like image 772
George Mauer Avatar asked Jun 16 '16 22:06

George Mauer


1 Answers

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.

like image 75
George Mauer Avatar answered Jan 04 '23 01:01

George Mauer