I'm trying to connect to a websocket established with SignalR on my server (.NET).
My client (JavaScript) starts the negotiation, gets a response with the connectionId, connectionToken, etc., but afterwards is not able to connect with any of the available transport methods.
The last debug-trace I get is this:
[2022-11-17T10:21:02.093Z] Debug: HubConnection failed to start successfully because of error 'Error: Unable to connect to the server with any of the available transports. WebSockets failed: Error: There was an error with the transport. ServerSentEvents failed: Error: Error occurred LongPolling failed: Error'.
My server code:
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Sample
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_3_0).AddNewtonsoftJson();
services.AddSignalR().AddNewtonsoftJsonProtocol(opt => {
opt.PayloadSerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.WithOrigins("http://localhost:8080", "http://127.0.0.1:8080")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseFileServer();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseCors("MyPolicy");
app.UseMvc();
app.UseRouting();
app.UseEndpoints(routes =>
{
routes.MapHub<Controllers.DesignAutomationHub>("/api/signalr/designautomation");
});
}
}
}
Controller:
using Autodesk.Forge;
using Autodesk.Forge.DesignAutomation;
using Autodesk.Forge.DesignAutomation.Model;
using Autodesk.Forge.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Sample.Controllers
{
[ApiController]
public class ServiceController : Controller
{
// Design Automation v3 API
DesignAutomationClient _designAutomation;
// Used to access the application folder (temp location for files & bundles)
private IWebHostEnvironment _env;
// used to access the SignalR Hub
private IHubContext<DesignAutomationHub> _hubContext;
public ServiceController(IWebHostEnvironment env, IHubContext<DesignAutomationHub> hubContext, DesignAutomationClient api)
{
_designAutomation = api;
_env = env;
_hubContext = hubContext;
}
}
/// <summary>
/// Class uses for SignalR
/// </summary>
public class DesignAutomationHub : Microsoft.AspNetCore.SignalR.Hub
{
public string GetConnectionId() { return Context.ConnectionId; }
}
}
Client:
var connection;
var connectionId;
function startConnection(onReady) {
if (connection && connection.connectionState) {
if (onReady) onReady();
return;
}
connection = new signalR.HubConnectionBuilder()
.withUrl(
"http://<SERVERADRESS>/api/signalr/designautomation"
)
.configureLogging(signalR.LogLevel.Trace)
.build();
connection.start().then(function () {
connection.invoke("getConnectionId").then(function (id) {
connectionId = id;
if (onReady) onReady();
});
});
connection.on("downloadResult", function (url) {
console.log('<a href="' + url + '">Download result file here</a>');
});
connection.on("onComplete", function (message) {
console.log(message);
});
}
I tested it locally with server and client on one machine, and all is working fine. But since the deployment test, I get the errors. (Websockets are activated on the server.)
Also Postman can establish a connection to the websocket, just my client fails.
I would appreciate any kind of help. Thanks in advance!
EDIT: I also tried connecting to SignalR via the (here) described alternative to the SignalR client-side.
async function connectToWebsocket(negotiations) {
let token = encodeURIComponent(negotiations.connectionToken);
let wssPath = `ws://<SERVERADRESS>/api/signalr/designautomation?id=${token}`;
let ws = new WebSocket(wssPath);
console.log(ws);
}
async function connectToSignalR() {
$.ajax({
url: "<SERVERADRESS>/api/signalr/designautomation/negotiate?negotiateVersion=1",
contentType: "application/json",
dataType: "json",
headers: { "Access-Control-Allow-Origin": "*" },
type: "POST",
success: function (res) {
console.log(res);
connectToWebsocket(res);
},
error: function (error) {
console.log(error);
},
});
}
Still with the same outcome. I get the response from the negotiation but cant connect to the websocket.
As an additional Information. My "server" is an iis-express on a Azure-VM with a via ngrok forwarded adress.
ANOTHER EDIT: My whole case about the use of SignalR is to get a websocket connection running, which the autodesk-forge servers can callback, when they finished my submitted task, to let them know what the next tasks are. I added the Tag, maybe someone from this direction encountered same problems and could give a hint.
AND ANOTHER EDIT: I now also tried the connection to the remote server with the simplest example I could find, the chatapp example from microsoft. Still the same problems. I added the whole console output here: Also I'm curious if theres maybe something wrong with my CORS. But it's defined as stated in every working example...
The problem resulted from ngrok and probably CORS (as stated in the console output).
After a push in the right direction, I tried another tool (Conveyor - VS Extension) forwarding localhost to the net. There was absolutely no issue with connecting to the websocket or any other transport method from a remote client.
So for anybody with the same problem, trying to debug websocket connections forwarded with ngrok in iis-express and getting CORS errors, use Conveyor instead. As long as you don't know how to configure ngroks CORS handling ("http --host-header=rewrite" wasn't doing the trick).
If you know how to configure ngroks CORS handling, I would be glad to read it in the comments.