Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR Error During Negotation Request When Testing Against Vagrant VM

I have setup a console application that just runs a loop and emits a message using Signal R. The client that is listening is an angular application.

When I run locally, (Both the Console application and the Angular Site) it works fine. However, when I run my console application in my Vagrant VM (Ubuntu HOST), then I get an all too familiar error message such as the following:

GET http://localhost:12345/signalr/negotiate?clientProtocol=1.5&userId=12345&connectionData=%5B%7B%22
name%22%3A%22testem
itter%22%7D%5D&_=1446565284280 500 (Internal Server Error)

I have ran into issues similar to this before (maybe this exact one) so here are some initial details

The code I have looks like the following:

  namespace test
  {
      public class Program
      {
          public static void Main(string[] args)
          {
              try
              {
                  using (WebApp.Start<EmmitStartup>("http://*:12345"))
                  {
                      Console.Out.WriteLine("Emmit started on http://*:12345");

                      IEmitterFactory factory = new EmitterFactory();
                      while (true)
                      {
                          Thread.Sleep(5000);
                          ITestEmitter emitter = factory.Create((ctx) =>
                              {
                                  return new TestEmitter(ctx);
                              });
                          emitter.SayHello();
                          emitter.Echo("Hello World:" + DateTime.Now);
                      }
                  }
              }
              catch (Exception e)
              {
                  Console.Out.WriteLine(e.Message);
                  Console.Out.WriteLine(e.StackTrace);
                  Console.ReadLine();
              }
          }
      }

      public class TestEmitter:Emitter,ITestEmitter
      {
          public TestEmitter(IEmitterContext emitterContext) : base(emitterContext)
          {
          }

          public TestEmitter(IEmitterContext emitterContext, EmitterModel model) : base(emitterContext, model)
          {
          }

          public TestEmitter(IDictionary<string, object> model) : base(model)
          {
          }

          public void SayHello()
          {
              EmitterContext.Clients.All.onSayHello();
          }

          public void Echo(string message)
          {
              EmitterContext.Clients.All.onEcho(message);
          }
      }

      public interface ITestEmitter
      {
          void SayHello();
          void Echo(string message);
      }
      public class EmmitStartup
      {
          public void Configuration(IAppBuilder app)
          {

              app.UseCors(CorsOptions.AllowAll);
              app.Map("/signalr", map =>
              {
                  map.UseCors(CorsOptions.AllowAll);
                  GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule());
                  var config = new HubConfiguration()
                  {
                      EnableDetailedErrors = true,
                      EnableJavaScriptProxies = true,
                      EnableJSONP = true
                  };
                  map.RunSignalR(config);
              });
          }
      }
  }
  1. There are no exception or error logs being thrown on the server.
  2. I have enabled CORS in SignalR
  3. I have tried to use both http://*:12345 and http://localhost:12345 and http://0.0.0.0:12345
  4. The Emmit library is just syntactic sugar and makes direct pass through to SignalR (I have tried same exact with SignalR directly.
  5. I have tried different combinations of enabling/disabling EnableJSONP
  6. I know SignalR is working and accessible through the VM because I can hit http://localhost:12345/signalr/hubs and it shows proxy file.
  7. I have setup port fowarding to the Vagrant VM for port 12345
  8. I have disabled firewall on VM HOST (Ubuntu) with sudo ufw disable

The code for the client looks like the following:

    var emmitProxy = null;
  Emmit.createProxy({
        emitter:'testEmitter',
        path:'http://localhost:12345/signalr',
        listeners:{
            'onSayHello':function(){
                $log.info('onSayHello triggered')
            },
            'onEcho':function(message){
              $log.info(message);
            }
        },
        onError:function(){
            //an error occured
            $log.error('testEmitter:onError');
        },
        onDisconnected:function(){
            //proxy was disconnected
            $log.debug('testEmitter:onDisconnected');
        },
        queryParams:{
            userId:'12345'  //optional
        }
    }).then(function(newProxy){
        emmitProxy = newProxy;
  });

UPDATE

I enabled logging and here is the output. Before another person recommends I enable CORS, I don't think that CORS is the issue, I think its just the cascading impact of something else that is having an issue.

enter image description here

UPDATE

I have ran this in multiple environments with the following results:

  1. Ran in Docker container on Vagrant VM (Ubuntu) - ERROR OCCURS
  2. Ran directly on Vagrant VM (Ubuntu) - ERROR OCCURS
  3. Deployed in Docker Container to Tutum - ERROR OCCURS
  4. Ran directly through Visual Studio on Windows - EVERYTHING WORKS
  5. Ran directly on Mac oSX (on Mono obviously) - EVERYTHING WORKS

I have added the following IHubPipelineModule

public class ErrorHandlingPipelineModule:HubPipelineModule
{
    public override Func<HubDescriptor, IRequest, bool> BuildAuthorizeConnect (Func<HubDescriptor, IRequest, bool> authorizeConnect)
    {
          try
          {
              Console.Out.WriteLine ("BuildAuthorizeConnect");
              return base.BuildAuthorizeConnect (authorizeConnect);
          }
          catch (Exception exception)
          {
              Console.Out.WriteLine ("AuthorizeConnect Failure");
              Console.Out.WriteLine(exception.Message);
          }
          return base.BuildAuthorizeConnect(authorizeConnect);
    }

    protected override void OnAfterDisconnect (IHub hub, bool stopCalled)
    {
        try
        {
            Console.Out.WriteLine ("OnAfterDisconnect");
            base.OnAfterDisconnect (hub, stopCalled);
            Console.Out.WriteLine ("After OnAfterDisconnect");
        }
        catch (Exception exception)
        {
          Console.Out.WriteLine ("AfterDisconnect Failure");
          Console.Out.WriteLine(exception.Message);
        }
    }

    protected override bool OnBeforeDisconnect (IHub hub, bool stopCalled)
    {
            try
            {
                Console.Out.WriteLine ("OnBeforeDisconnect");
                return base.OnBeforeDisconnect (hub, stopCalled);
            }
            catch (Exception exception)
            {
                Console.Out.WriteLine ("BeforeDisconnect Failure");
                Console.Out.WriteLine(exception.Message);
            }
            return base.OnBeforeDisconnect (hub, stopCalled);
    }


    public override Func<IHub, System.Threading.Tasks.Task> BuildConnect(Func<IHub, System.Threading.Tasks.Task> connect)
    {
          try
          {
              Console.Out.WriteLine("BuildConnect");
              return base.BuildConnect(connect);
          }
          catch (Exception exception)
          {
              Console.Out.WriteLine(exception.Message);
          }
          return base.BuildConnect(connect);
    }

    protected override void OnAfterConnect(IHub hub)
    {
        try
        {
            Console.Out.WriteLine("OnAfterConnect");
            base.OnAfterConnect(hub);
      }
        catch (Exception exception)
        {
            Console.Out.WriteLine ("OnAfterConnect Failure");
            Console.Out.WriteLine(exception.Message);
        }
    }

    protected override bool OnBeforeConnect(IHub hub)
    {
        try
        {
            Console.Out.WriteLine("OnBeforeConnect");
            return base.OnBeforeConnect(hub);
        }
        catch (Exception exception)
        {
            Console.Out.WriteLine ("OnBeforeConnect Failure");
            Console.Out.WriteLine(exception.Message);
        }
        return base.OnBeforeConnect (hub);
    }
}

And when I check my logs, the only logs that are printed out are the following:

 BuildConnect
 BuildAuthorizeConnect

UPDATE I am not sure if this will be relevant or how I may have missed it, but I checked the response from the 500 and have posted it to Here

It looks like it is showing it is related to Improperly protected user's key pairs in '/root/.config/.mono/keypairs'.

Also, I am not sure if this link contains sensitive information. If somebody can let me know if it does, I would appreciate it.

I did a minimal amount of research thus far and came across SignalR.Owin works under Windows but returns 500 for Mono on Linux

When I check the Network tab for the negotiate request, I get the following

** Headers** Remote Address:127.0.0.1:12345 Request URL:http://localhost:12345/signalr/negotiate?clientProtocol=1.5&userId=12345&connectionData=%5B%7B%22name%22%3A%22testemitter%22%7D%5D&_=1446964166640 Request Method:GET Status Code:500 Internal Server Error

    HTTP/1.1 500 Internal Server Error
    Access-Control-Allow-Origin: http://localhost:8100
    Access-Control-Allow-Credentials: true
    X-Content-Type-Options: nosniff
    Content-Type: text/html
    Server: Mono-HTTPAPI/1.0
    Date: Sun, 08 Nov 2015 06:30:28 GMT
    Connection: close
    Transfer-Encoding: chunked

    Accept:text/plain, */*; q=0.01
    Accept-Encoding:gzip, deflate, sdch
    Accept-Language:en-US,en;q=0.8
    Cache-Control:no-cache
    Connection:keep-alive
    Content-Type:application/x-www-form-urlencoded; charset=UTF-8
    Cookie:JSESSIONID.ae4b31f4=aqpz31hevinaauwftyijure; JSESSIONID.26c95422=1m32u58exuvjz5jrojszqziqh; JSESSIONID.3fd19426=iv9fawaej3nt14yzcruj45si5; JSESSIONID.8868ba42=1gh4w06alx8ehuuj1adr5w8y8; JSESSIONID.947cfb91=nyxfrp6u0pny1sl8gwlouprh4; screenResolution=1280x800
    Host:localhost:12345
    Origin:http://localhost:8100
    Pragma:no-cache
    Referer:http://localhost:8100/
    User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36
like image 302
TheJediCowboy Avatar asked Nov 03 '15 16:11

TheJediCowboy


1 Answers

Although your app contains app.UseCors(CorsOptions.AllowAll), the original error message is pretty clear that this is a CORS issue.

Original Error message

The Origin URL (http://localhost:8100) is different than the SignalR server URL (http://localhost:12345) With CORS, the scheme, domain, and port have to match; in this case, the port is different.

Here's what to do:

  1. Ensure the URLs are actually correct and are what you expect. (Port 8100)
  2. Capture the HTTP traffic on the client. You can do this using Fiddler or a similar tool. You can also just use the "network" tab of the chrome debugger.
  3. Verify that the initial page load actually contains the CORS header. You should see Access-Control-Allow-Origin: in the response header. Based on how you've configured it, the value should be: *

...I believe the issue here is that you've enabled CORS on the SignalR server (Port 12345), but CORS needs to be enabled on your webserver. (Port 8100)

EDIT

The original question was edited. With the addition of the HTTP headers, the error message has changed from a CORS issue to a 500 error.

CryptographicException: Improperly protected user's key pairs in '/root/.config/.mono/keypairs'.
...
CryptographicException: Data protection failed.
System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) [0x00000]
Microsoft.AspNet.SignalR.Infrastructure.DefaultProtectedData.Protect (System.String data, System.String purpose) [0x00000]

I did a minimal amount of research thus far and came across SignalR.Owin works under Windows but returns 500 for Mono on Linux

The GitHub Issue that you linked to exactly matches what you've described. Since it's marked closed, the obvious question is to see if what's described in the ticket also applies to you. From the ticket:

davidfowl commented on May 22, 2013 - I just fixed the build in the dev branch yesterday. It requires mono 3.0.10 to run and works fine now.

So, verify two things:

  1. The version of Mono installed on your server. The Ticket says Mono 3.0.10 is needed. What version do you have?
  2. SignalR needs to include the changes. It's one or both of these. Ensure that whatever version of SignalR you're using includes these changes:
    • Change 1
    • Change 2

Lastly, if you need to, you do have the option of injecting your own version of SignalR.Core/Infrastructure/DefaultProtectedData.cs. You can simply set it in the OWIN pipeline. From the SignalR Source Code:

           // Use the data protection provider from app builder and fallback to the
           // Dpapi provider
           IDataProtectionProvider provider = builder.GetDataProtectionProvider();
           IProtectedData protectedData;
like image 189
Jared Dykstra Avatar answered Oct 06 '22 00:10

Jared Dykstra