I am trying to implement signalR to see who of my users are currently connected. i get connected then immediately after I get an error saying that the connection disconnected:
This is what I have got :
A "presence hub" :
public class PresenceHub : Hub
{
private readonly PresenceTracker _tracker;
public PresenceHub(PresenceTracker tracker)
{
_tracker = tracker;
}
public override async Task OnConnectedAsync()
{
await _tracker.UserConnected(Context.User.GetUsername(), Context.ConnectionId);
await Clients.Others.SendAsync("UserIsOnline", Context.User.GetUsername());
var currentUsers = await _tracker.GetOnlineUsers();
await Clients.All.SendAsync("GetOnlineUsers", currentUsers);
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await _tracker.UserDisconnected(Context.User.GetUsername(), Context.ConnectionId);
await Clients.Others.SendAsync("UserIsOffline", Context.User.GetUsername());
var currentUsers = await _tracker.GetOnlineUsers();
await Clients.All.SendAsync("GetOnlineUsers", currentUsers);
await base.OnDisconnectedAsync(exception);
}
}
A "presence tracker" to track the connected users :
public class PresenceTracker
{
private static readonly Dictionary<string, List<string>> OnlineUsers = new Dictionary<string, List<string>>();
public Task UserConnected(string username, string connectionId)
{
lock (OnlineUsers)
{
if (OnlineUsers.ContainsKey(username))
{
OnlineUsers[username].Add(connectionId);
}
else
{
OnlineUsers.Add(username, new List<string>{ connectionId });
}
}
return Task.CompletedTask;
}
public Task UserDisconnected(string username, string connectionId)
{
lock (OnlineUsers)
{
if(!OnlineUsers.ContainsKey(username)) return Task.CompletedTask;
OnlineUsers[username].Remove(connectionId);
if (OnlineUsers[username].Count == 0)
{
OnlineUsers.Remove(username);
}
}
return Task.CompletedTask;
}
public Task<string[]> GetOnlineUsers()
{
string[] onlineUsers;
lock (OnlineUsers)
{
onlineUsers = OnlineUsers.OrderBy(k => k.Key).Select(k => k.Key).ToArray();
}
return Task.FromResult(onlineUsers);
}
}
}
The addAuthentication service :
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Token:Key"])),
ValidIssuer = builder.Configuration["Token:Issuer"],
ValidateIssuer = false,
ValidateAudience = false
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("hubs"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
This is my service for the presence :
createHubConnection(user: User) {
this.hubConnection = new HubConnectionBuilder()
.withUrl(this.hubUrl + 'presence', {
accessTokenFactory: () => user.token,
})
.withAutomaticReconnect()
.build()
this.hubConnection.start().then(() => {console.log("connection to hub successful") }).catch(error => console.log(error));
this.hubConnection.on('UserIsOnline', username => {
this.toastr.info(username + " has connected");
})
this.hubConnection.on('UserIsOffline', username => {
this.toastr.info(username + " has disconnected");
})
this.hubConnection.on('GetOnlineUsers ', usernames => {
this.onlineUsersSource.next(usernames);
})
}
stopHubConnection() {
this.hubConnection?.stop().catch(error => console.log(error));
}
I have tried multiple things amongst which are these middlewares :
app.UseCors(builder =>
{
builder.WithOrigins("https://localhost:4200")
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyHeader();
});
app.MapHub<PresenceHub>("/hubs/presence");
app.UseWebSockets();
builder.Services.AddSignalR(o =>
{
o.EnableDetailedErrors = true;
o.MaximumReceiveMessageSize = 102400000;
});
builder.Services.AddSingleton<PresenceTracker>();
