Cross origin SignalR connection terminates after negotiation

I have a viewer MVC 5 application and a Web API 2 application as a service level (.NET 4.5). The Web API application uses SignalR 2.1.2 to return progress when processing POST in the services API. These two applications are deployed in different domains, so I installed cross-education support in accordance with the asp.net tutorial.

[assembly: OwinStartup(typeof (Startup))] namespace MyApp.Service { public class Startup { public void Configuration(IAppBuilder app) { app.Map("/signalr", map => { //worry about locking it down to specific origin later map.UseCors(CorsOptions.AllowAll); map.RunSignalR(new HubConfiguration()); }); //now start the WebAPI app GlobalConfiguration.Configure(WebApiConfig.Register); } } } 

WebApiConfig.cs also contains its own CORS declaration.

 namespace MyApp.Service { public static class WebApiConfig { public static void Register(HttpConfiguration config) { //controller invocations will come from the MVC project which is deployed to a //different domain, so must enable cross origin resource sharing config.EnableCors(); // Web API routes config.MapHttpAttributeRoutes(); //Snip other controller dependency initialisation } } } 

I defined a simple hub class with no server-side API (it only allows the server to click on clients, not on client calls).

 namespace MyApp.Service.Hubs { [HubName("testresult")] public class TestResultHub : Hub { } } 

Since I am moving to a cross-domain and the hub does not expose any API side of the server, I will not try to use the generated JS proxy.

The relevant JS bits that establish the signalr hi-connection are: (remember that this is done from an MVC application that does not have signalr support (except for jquery-signalr- {version} .js, of course))

 function TestScenarioHandler(signalrHubUrl) { var self = this; //Snip irrelevant bits (mostly Knockout initialisation) self.signalrConnectionId = ko.observable(); var hubConnection = $.hubConnection(signalrHubUrl, { useDefaultPath: false }); var hubProxy = hubConnection.createHubProxy("testresult"); hubProxy.on("progress", function(value) { console.log("Hooray! Got a new value from the server: " + value); }); hubConnection.start() .done(function() { self.signalrConnectionId(hubConnection.id); console.log("Connected to signalr hub with connection id " + hubConnection.id); }) .fail(function() { console.log("Failed to connect to signalr hub at " + hubConnection.url); }); } 

Crossing this line, Firefox shows network traffic (and I confirmed that Chrome shows the same thing) GET to

http://****service.azurewebsites.net/signalr/negotiate?clientProtocol=1.5&connectionData=[{"name":"testresult"}]&_=1424419288550

Note that name matches the value of the HubName attribute for my hub class.

This GET returns HTTP 200, the response gives me a JSON payload containing ConnectionId , ConnectionToken , and a bunch of other fields that offer everything in order. The HTTP response also has an Access-Control-Allow-Origin: header set for the domain from which GET originated. It all looks good, except that the traffic stops.

But the JS console prints "Failed to connect to signalr hub at http://****service.azurewebsites.net/signalr"

To make sure I'm not doing anything stupid, I added signalr support and a base hub to the MVC application (therefore, no cross origin was required), and changed the calls to $.hubConnection() and hubConnection.createProxy() . When I do this, the browser traffic shows the same /signalr/negotiate?... GET (obviously not a cross-source), but then also appears on /signalr/connect?... and /signalr/start?... . The JS console also displays a success message.

So in short;

  • CORS is enabled at the service level, and signalr /negotiate GET returns 200, which is a valid connection identifier and the expected Access-Control-Allow-Origin: header. This tells me that server side CORS support behaves correctly, but the signalr connection fails.
  • When I reconfigure so that the signalr connection is not cross, everything works as expected.

WTF am I missing or doing wrong ?! Is there any conflict between HttpConfiguration.EnableCors() and IAppBuilder.UseCors(CorsOption) maybe?

+6
source share
1 answer

I decided. I changed map.UseCors(CorsOptions.AllowAll) to pass the CorsPolicy object instead, and set SupportsCredentials to false, reading elsewhere that Access-Control-Allow-Origin: * incompatible with access-control-allow-credentials: true .

 private static readonly Lazy<CorsOptions> SignalrCorsOptions = new Lazy<CorsOptions>(() => { return new CorsOptions { PolicyProvider = new CorsPolicyProvider { PolicyResolver = context => { var policy = new CorsPolicy(); policy.AllowAnyOrigin = true; policy.AllowAnyMethod = true; policy.AllowAnyHeader = true; policy.SupportsCredentials = false; return Task.FromResult(policy); } } }; }); public void Configuration(IAppBuilder app) { app.Map("/signalr", map => { map.UseCors(SignalrCorsOptions.Value); map.RunSignalR(new HubConfiguration()); }); //now start the WebAPI app GlobalConfiguration.Configure(WebApiConfig.Register); } 

Setting SupportCredentials to true results in the Access-Control-Allow-Origin header, which overwrites with the actual beginning (not * ) and access-control-allow-credentials: true in the response.

And now it works.

+6
source

Source: https://habr.com/ru/post/982770/


All Articles