SignalR firing OnConnectedAsync for users in a hub on another machine - asp.net-core

Our Azure SignalR application is a bit of a hybrid, outgoing messages go into ActiveMQ then get picked up by a listener in a hosted service that receives them and sends them to the NotificationsHub (in the same API project). e.g. await _notificationsHub.Clients.All.SendAsync("PerformAction", action, payload);
Recently I tried to extend this so that a second application using a different authentication scheme (this one is external facing, the existing one is internal facing) could connect and receive some messages (and send others back). I did a POC and everything seemed fine.
public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub {
public override async Task OnConnectedAsync() {
await Groups.AddToGroupAsync(Context.ConnectionId, "foo");
await base.OnConnectedAsync();
await Clients.Caller.SendAsync("SetConnectionId", Context.ConnectionId);
}
}
Today I was debugging in Visual Studio the new application API and in the Hub's OnConnectedAsync method I kept receiving the "connected" events for users on a completely different server.
So my question is, does SignalR fire the "OnConnected" event for every hub connected to the same endpoint (for every instance of that hub)?
Even if that is the case wouldnt these two Hub classes be considered separate and not share their groups (see multiple hubs)?
Edit
For reference the project is targeting netcoreapp3.1, and using Microsoft.Azure.Signalr 1.8.1 in the original project and 1.16.1 in the new one.

It turns out that SignalR uses the name of the "Hub" class when connecting to the Azure SignalR service, i.e. 'wss://foo.service.signalr.net/server/?hub=notificationshub&cid={guid}'.
All hubs with the same name are treated as the same hub. I couldnt find that in the docs anywhere but it is implied in ApplicationName property of ServiceOptions which says "Gets applicationName, which will be used as a prefix to apply to each hub name"

Related

Random ASP.NET Core Identity Logouts in production

I have an ASP.NET Core MVC web application that uses Identity to handle user account Authentication and Authorization. When running the app on my local IIS express everything was working properly. After deploying the app to a shared web server I started to notice that the logged-in user accounts would get logged out at seemingly random intervals. Through experimentation, I was able to determine that the log-outs were occurring whether the account was active or idle. They were occuring at no recuring time interval and completely unrelated to any expiry time that I set on my cookies. The logouts were occuring on every view in the web app so I couldn't pin the issue to any particular controller. Also I use the same Database for the published and the local testing version of the app and therefore the same user accounts. I anyone has an idea where to start looking for a solution it would be greatly appreciated.
I posted this question because there is a great answer that 90% solves the issue Here however of the multiple forums that I have been scouring over the last few days there are none with an accepted answer. I am posting this answer to address this. The underlying cause of the issue is that IIS application pool is being reset or recyling and on a shared host with multiple applications using it this can be happening fairly frequently. As is suggested in the above link Data Protection has to be used to persist the keys if IIS application pool recycles. Here is the code offered in the original answer.
services.AddDataProtection()
.PersistKeysToFileSystem(new System.IO.DirectoryInfo("SOME WHERE IN STORAGE"))
//.ProtectKeysWithCertificate(new X509Certificate2());
.SetDefaultKeyLifetime(TimeSpan.FromDays(90));
This code is to be added in ConfigureServices in Startup.cs
As my application is being hosted on a shared server using .PersistKeysToFileSystem was not an option so instead I persisted the keys using DbContext like this:
services.AddDataProtection().PersistKeysToDbContext<MyKeysContext>()
.SetDefaultKeyLifetime(TimeSpan.FromDays(90));
Based on This article here I build MyKeysContext as follows.
// Add a DbContext to store your Database Keys
services.AddDbContext<MyKeysContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("MyKeysConnection")));
In ConfigureServices in Startup.cs and then created a class called MyKeysContext as follows:
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;
namespace WebApp1
{
public class MyKeysContext : DbContext, IDataProtectionKeyContext
{
// A recommended constructor overload when using EF Core
// with dependency injection.
public MyKeysContext(DbContextOptions<MyKeysContext> options)
: base(options) { }
// This maps to the table that stores keys.
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
}
}
I created the database on my Host this will probably be different so I have omitted this step. then I applied the migrations to the database like this.
Add-Migration AddDataProtectionKeys -Context MyKeysContext
Update-Database -Context MyKeysContext

Cleint not receive response: “No handlers could be found for message type”

I have a .net core service that needs to send a request via nservicebus.
I resolve IMessageSession with DI and send a request like this:
var response = await messageSession.Request<CreateDeviceResponse>(request);
In the logs I see that another service received this request and send a reply:
The problem that I never receive a response.
Client receive such errors:
I know that such an issue can occur if the client and server endpoint names are same, but I checked and I use different names
asp net mvc 4.7.2
First of all, the message isn't lost, but in the error queue.
I suggest to follow the tutorial to understand how NServiceBus works and what you need to have in place where. The tutorials start here but an entire learning path starts here and it explains broader, including about general concepts and architecture.
Like #tchelidze mentions, you need to have an implementation of the IHandleMessages<CreateDeviceReponse> interface, like is outlined here in the tutorial. Also, it doesn't matter to which endpoint you send it to. EndpointA can send to both EndpointB and EndpointA. What you cannot do is have two endpoints with the same name. It's like having two computers with the same name called MachineA and then from MachineB try to access files on MachineA. Which of the two should MachineB connect to? There is something else called 'competing consumer pattern', but that's of later concerns ;-)
If you need more help, feel free to ask at https://discuss.particular.net/ where more NServiceBus community members are located, as well as engineers of NServiceBus itself.
For some reason, it helped to make a request inside a task:
Task.Run(async () => await messageSession.Request<CreateDeviceResponse>(request))
I still not understand what is the problem, but it works only with such implementation.
In .net core web API, it works even without Task.Run, so I believe it has something to do with the fact that I am making this request in asp net mvc 4.7.2

SAP Cloud SDK JS receives empty data from Enterprise Messaging Queue

I have build a simple cloud-SDK based application for adding as a Webhook in Enterprise messaging queue to receive the events as soon as it gets inside the queue.
I have an OPTIONS and POST function. OPTIONS is for the handshake with the queue and it works.
Now, when there is message in the Queue, it hits my application with the POST block but the request body is coming as empty object.
I have tried the same from the postman, i'm able to receive the data in request body. Only from the Enterprise messaging queue, the data is empty.
In contrast, to verify this, I have used a Express based nodejs application, there i'm able to receive the data from the queue.
What am i missing in the Cloud-SDK based code ?
POST block, looks like this
#Post('ems-events')
receiveEmsEvents(#Body() requestBody: string, #Req() req:Request) {
Logger.log("Event Received with Data:");
Logger.log(requestBody);
Logger.log(req.body);
Logger.log("Log over--");
Logger.log(Object.keys(req));
return {};
}
The SAP Cloud SDK for JavaScript does not offer any support for Enterprise Messaging as of today. The code you're writing here is most likely Nest.js code, which is an independent framework.
That being said, Nest.js does run Express.js under the hood by default. So if you've been able to make it work in Express, you should be able to make it work in Nest.

WinRT - consume WCF service

I have a windows service, which acts as a propagator for data received from external source. This windows service hosts WCF ServiceHost with NetNamedPipeBinding(). ServiceContract also defines CallbackContract
There is also a client DLL component, which consumes the server and bubbles up parsed data as an event. Data is bubbled upon receiving callback from the server.
The code works in desktop app, however when I try to reference client DLL in WinRT app I get following error:
The pipe name could not be obtained for the pipe URI: Access is denied. (5, 0x5)
I presume this is because WinRT (to my knowledge) lacks support for named pipes.
How to go about consuming such service in WinRT? I can alter WCF side to any requirement, but it has to be hosted as windows service (it has non WinRT consumers). Communication will always occur within the same machine, polling is the last resort.
First you need to switch to basicHttpBinding, because net.namedpipe is not supported.
Actually supported are BasicHttpBinding, NetTcpBinding, NetHttpBinding
Secondly in WinRT there's a policy in place, which prevents you from accessing localhost over the network stack.
To overcome this security policy you need to add a LoopbackExempt for your app.
CheckNetIsolation.exe LoopbackExempt -s
See details on MSDN:
http://msdn.microsoft.com/en-us/library/windows/apps/Hh780593.aspx
For the duplex way either POLLING is an aption (only works, when the app is focused).
Or using push notifications: http://blogs.msdn.com/b/jimoneil/archive/2012/10/15/windows-8-notifications-push-notifications.aspx
Use the HttpClient class.. that's the only simple workaround, and works as well.
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
httpClient.DefaultRequestHeaders.Add("SOAPAction", "http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP");
var soapXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><GetCityWeatherByZIP xmlns=\"http://ws.cdyne.com/WeatherWS/\"><ZIP>23454</ZIP></GetCityWeatherByZIP></soap:Body></soap:Envelope>";
var response = httpClient.PostAsync("http://wsf.cdyne.com/WeatherWS/Weather.asmx", new StringContent(soapXml, Encoding.UTF8, "text/xml")).Result;
var content = response.Content.ReadAsStringAsync().Result;
Try this? I hope this is what you're looking for - https://quirkd.wordpress.com/2015/01/24/shorts-consuming-a-wcf-asmx-web-service-in-winrt/

Windows service using WCF NetNamedPipe to communicate to client

The reason I'm writing this question is that I seem to be getting the following error when I'm trying to communicate between a windows service and a WPF app via a WCF service with a NetNamedPipe binding:
System.ServiceModel.EndpointNotFoundException: There was no endpoint
listening at net.pipe://localhost/Pipe_SendInfo
Now the gory details.
Ok, I have a windows service that is periodically executing code, I wanted to let a user know what is happening inside the service. So I read that I could accomplish this via NetNamedPipe WCF service. I created two test apps and successfully was able to send a message from one process to another. I then attempted to send messages from the windows service to a client app(on the same machine) and have so far failed miserably :(.
My windows service essentially does this(trying to send info):
ChannelFactory<SkipSyncLib.ISendInfo> pipeFactory =
new ChannelFactory<SkipSyncLib.ISendInfo>(
new NetNamedPipeBinding(),
new EndpointAddress("net.pipe://localhost/Pipe_SendInfo"));
pipeFactory.CreateChannel();
pipeFactory.SendInfo(info);
and the application that is supposed to receive the information does this when it starts up:
public void Start()
{
HostService = new ServiceHost(this, new Uri[] { new Uri("net.pipe://localhost") });
HostService.AddServiceEndpoint(typeof(ISendInfo), new NetNamedPipeBinding(), "Pipe_SendInfo");
try
{
HostService.Open();
}
catch (Exception exc)
{
//Error handling
}
}
The kicker is that while the windows service is failing miserably to find the endpoint I have another console app that is able to send info to the running client app successfully. So logic would tell me that it probably has something to do with users. But I can't figure it out.
Any ideas? Should I just drop named pipes and go with an http binding?
I just found out about named pipes earlier today so please be gentle if the answer is obvious.
Thanks
I figured out a way for it to work. There's probably a more elegant way to do this, but if the windows service and the client app are run as the same user then the communication channel works.
Should you use the same endpoint address?
HostService = new ServiceHost(this, new Uri[] { new Uri("net.pipe://localhost/Pipe_SendInfo") });
add /Pipe_SendInfo in Uri