Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Self-Host or Emulate Azure SignalR Service for serverless local development #969

Closed
ryboucher opened this issue Jul 21, 2020 · 52 comments · Fixed by #1149 or #1192
Closed

Self-Host or Emulate Azure SignalR Service for serverless local development #969

ryboucher opened this issue Jul 21, 2020 · 52 comments · Fixed by #1149 or #1192

Comments

@ryboucher
Copy link

Is your feature request related to a problem? Please describe.

I am trying to self-host an instance of Azure SignalR service for use with my Azure functions project. This would allow me to continue working on my project even when azure servers are down.

Describe the solution you'd like

A sample on how we could emulate or self-host azure signalR service would be great. I really want to be able to work on my Azure functions project without needing to connect to azure directly.

@ModernRonin
Copy link

yes, I am looking for the exact same thing

@vicancy
Copy link
Member

vicancy commented Jul 31, 2020

Is Upstream used in your scenario?

If we have a very first version of the emulator that only supports SignalR Service output binding, would that help?

@ModernRonin
Copy link

For me, that would be sufficient.

@ryboucher
Copy link
Author

Same for me, I am only using output. Is that available?

@vicancy
Copy link
Member

vicancy commented Aug 3, 2020

I am trying to have a very first version this week for you to have a try.

@vicancy
Copy link
Member

vicancy commented Aug 24, 2020

Emulator Preview Version

Features available

  1. Auth
  2. Latest Rest API support
  3. Upstream

NOTE

It is only for serverless scenarios.

Walkthrough

Take this serverless sample for example: https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-azure-functions-javascript?WT.mc_id=signalrquickstart-github-antchu

  1. The first step Create an Azure SignalR Service instance can now be replaced by using the emulator
  2. Install the emulator
dotnet tool install  -g Microsoft.Azure.SignalR.Emulator --version 1.0.0-preview1-10604 --add-source https://www.myget.org/F/azure-signalr-dev/api/v3/index.json
  1. Run the emulator
asrs-emulator

After the emulator is successfully started, it generates the ConnectionString to be used later, for example, the ConnectionString is Endpoint=http://localhost;Port=8088;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGH;Version=1.0; as the below screenshot shows.

image

  1. Continue with the step Configure and run the Azure Function app as described in https://docs.microsoft.com/azure/azure-signalr/signalr-quickstart-azure-functions-javascript?WT.mc_id=signalrquickstart-github-antchu#configure-and-run-the-azure-function-app, instead of copying the connection string from the Azure Portal, use the ConnectionString generated by the emulator to fill into the value of the AzureSignalRConnectionString in your local.settings.json. Save the file and run the function locally with func start.

  2. Continue with the step Run the web application as described in https://docs.microsoft.com/azure/azure-signalr/signalr-quickstart-azure-functions-javascript?WT.mc_id=signalrquickstart-github-antchu#run-the-web-application

  3. The demo should work with the clients connected to localhost:8088

image

@AndreasFurster
Copy link

I've just got Upstream working in local development!
I'm still using the SignalR service in the cloud and ngrok to forward all upstream requests to my local network.

1. Authentication
SignalR authenticates with your functions app with a key based on SignalR's connectionstring (access key). I could not get to generate that key myself so I got the it from the functions app running in Azure.
image

2. Public URL for local function
Your local running instance has to use the same connectionstring as the the functions app in Azure!

First start the functions app: func start

Then I used ngrok to make my local running function available to the internet:
ngrok http -host-header=localhost 7071
Find the public url in the ngrok output. That url is forwarded to your local function.
Go to http://xxxxxxxxxx.ngrok.io/runtime/webhooks/signalr?code=test in your browser. This should result in a http status 500.

3. Setup Upstream
Then use the same url in the SignalR upstream settings. Use the key gathered in step 1 as the code.

image

4. SignalR messages send to the SignalR Service are now forwarded to your local running functions app!

This is the code I use to trigger a function with a SignalR message and send back another message:

[FunctionName(nameof(GetSingleTaskList))]
public static void Run(
    [SignalRTrigger(Constants.SignalRTasksHubName, "messages", GetSingleTaskListMessage.MethodName, ConnectionStringSetting = Constants.SignalRConnectionStringSetting)] InvocationContext invocationContext,
    [SignalRParameter] GetSingleTaskListMessage argument,
    [CosmosDB(Constants.CosmosDbDatabase, Constants.CosmosDbTasksCollection, Id = "{argument.Id}", PartitionKey = "{argument.Id}", ConnectionStringSetting = Constants.CosmosDbConnectionStringSetting)] QuickTaskList taskList,
    [SignalR(HubName = Constants.SignalRTasksHubName, ConnectionStringSetting = Constants.SignalRConnectionStringSetting)] IAsyncCollector<SignalRMessage> signalRMessages,
    ILogger log)
{
    signalRMessages.AddAsync(new SignalRMessage
    {
      ConnectionId = invocationContext.ConnectionId,
      Target = GotSingleTaskListMessage.MethodName,
      Arguments = new [] { new GotSingleTaskListMessage(taskList) }
    });
}

SignalR triggers docs

5. (optional) create a separate development SignalR Service
Because upstream only sends a request to the first matched url, you cannot use the same SignalR service for development and production (or you have to use a different hubname for development).

I opted to create a separate SignalR Service for development. You also have to create another functions app instance to get the access key.

Using this in combination with hot reloading the local functions instance is going great so far.

@vicancy
Copy link
Member

vicancy commented Sep 14, 2020

Emulator Preview With Upstream available

Features available

  1. Auth
  2. Latest Rest API support
  3. Upstream

Walkthrough

Take this serverless sample for example https://github.com/Azure/azure-functions-signalrservice-extension/tree/master/samples/bidirectional-chat

  1. Clone the sample repo to local
git clone https://github.com/Azure/azure-functions-signalrservice-extension.git
cd azure-functions-signalrservice-extension/samples/bidirectional-chat
  1. Install the emulator
dotnet tool install  -g Microsoft.Azure.SignalR.Emulator --version 1.0.0-preview1-10719 --add-source https://www.myget.org/F/azure-signalr-dev/api/v3/index.json

Or update the emulator to the latest preview version if it is already installed:

dotnet tool update -g Microsoft.Azure.SignalR.Emulator --version 1.0.0-preview1-10719 --add-source https://www.myget.org/F/azure-signalr-dev/api/v3/index.json
  1. Run the emulator asrs-emulator to list all the available commands:
    image

  2. Init the default upstream settings using:

asrs-emulator upstream init

It inits a default settings.json into the current folder, with a default upstream UrlTemplate as http://localhost:7071/runtime/webhooks/signalr, which is the URL for SignalR's local function trigger. When the file is modified, its change will be hot loaded into the emulator.

  1. Start the emulator
asrs-emulator start

After the emulator is successfully started, it generates the ConnectionString to be used later, for example, the ConnectionString is Endpoint=http://localhost;Port=8888;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGH;Version=1.0; as the below screenshot shows.

image

  1. Go into subfolder csharp and rename local.settings.sample.json to local.settings.json, use the ConnectionString generated by the emulator to fill into the value of the AzureSignalRConnectionString in your local.settings.json. Fill into AzureWebJobsStorage your storage connection string, for example, UseDevelopmentStorage=true when using storage emulator. Save the file and run the function in the csharp subfolder with func start.

image

  1. In the browser, navigate to http://localhost:7071/api/index to play with the demo.

image

@moodmosaic
Copy link

Is the emulator working also with Default/Classic mode or only with Serverless mode? I've just migrated to Azure SignalR and went with Default on the portal, which seems to work fine, but not when using the emulator.

@vicancy, I'm on 1.0.0-preview1-10624 if that helps.

Azure SignalR Service is not connected yet, please try again later.
Error: Failed to complete negotiation with the server: Error: Internal Server Error

@vicancy
Copy link
Member

vicancy commented Oct 9, 2020

No only serverless mode. For Default mode, please switch to use self-host SignalR, e.g. for netcore31, you can disable Azure SignalR through config:

{
  "Azure": {
    "SignalR": {
      "Enabled": false
    }
  }
}

@vicancy vicancy changed the title Self-Host or Emulate Azure SignalR Service for local development Self-Host or Emulate Azure SignalR Service for serverless local development Oct 9, 2020
@moodmosaic
Copy link

moodmosaic commented Oct 9, 2020

please switch to use self-host SignalR

@vicancy, could you point me at some (recent, up to date) docs regarding that topic? Edit: Do you mean that in Debug/Local I can simply omit

AddAzureSignalR

?

@vicancy
Copy link
Member

vicancy commented Oct 10, 2020

Do you mean that in Debug/Local I can simply omit AddAzureSignalR

Yes

@amura11
Copy link

amura11 commented Oct 26, 2020

@vicancy is there a way to allow the emulator to work with remote connections? I'm trying to use the emulator on my local machine with a Xamarin application running on an Android emulator. Using localhost in the connection string doesn't work as I'm assuming that causes SignalR to try and connect to the Android emulator and not my local PC. I tried using the IP on my machine but that times out. I have an exception in my windows firewall for port 8888 but looking at netstat it looks like the emulator is binding to the local IP address.

Edit
@vicancy I just saw that you put in a pull request for this, I really appreciate you doing that so quickly! 🎉

@Chappitono
Copy link

Hello everyone, just wanted to add a few words on something I struggled with, using @vicancy's nice emulator but that I did manage to solve (somehow). If that may help :

Problem : My javascript invoke method didn't work :
this.hubConnection.invoke("JoinUserToGroup", "username", "groupName");

asrs-emulator

1.0.0-preview1-10624

Localhost Functions Server

  • Azure Functions Core Tools (3.0.2931 Commit hash: d552c6741a37422684f0efab41d541ebad2b2bd2)
  • Function Runtime Version: 3.0.14492.0
  • PackageReference Include="Microsoft.Azure.WebJobs.Extensions.SignalRService" Version="1.2.2"
  • PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.9"

With a simple Hub class that extends ServerlessHubs with a Negotiate method

And a simple function my client will call using a SignalRTrigger

        [FunctionName(nameof(JoinUserToGroup))]
        public async Task JoinUserToGroup([SignalRTrigger]InvocationContext invocationContext, string userName, string groupName, ILogger logger)
        {
            await UserGroups.AddToGroupAsync(userName, groupName);
        }

Resolution :
I received a log message from the emulato but the response.reason was missing so I ran it locally.

The problem is that the negotiate method returns a token in the connectionInfo with some claims considered as duplicate.
So I just filtered them out and now it works.

        [FunctionName(nameof(Negotiate))]
        public SignalRConnectionInfo Negotiate([HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req)
        {
            var authClaims = GetClaims(req.Headers["Authorization"]);

            authClaims.Where(c => c.Type == "aud" || c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" || c.Type == "scope").
                ToList().ForEach(c => authClaims.Remove(c));

            **var connectionInfo = Negotiate(null, authClaims);**

            return connectionInfo;
        }

Please note, that it may be a wrong solution or a misunderstanding of how SignalR Service works.
Hope that may help

@vicancy
Copy link
Member

vicancy commented Oct 30, 2020

Hi @amura11 With the latest preview version 1.0.0-preview1-10652, you should be able to configure the IP using asrs-emulator start --ip <your_ip>

Hi @Chappitono Thanks for the sharing, so is this an issue for the Emulator only?

@Chappitono
Copy link

Chappitono commented Nov 12, 2020

Sorry for my late answer @vicancy

Update : I have got the same invocation problem after deploying on Azure :
image

I guess I may have an issue with the bearer token (In the negotiate method, I am sending the same token I got from IdentityServer4).

So I will start reading again your samples and doc to find out the problem.
Thanks for reading

@vicancy
Copy link
Member

vicancy commented Nov 13, 2020

Thanks @Chappitono for the updates, this might be an issue that our service side need to handle, @zackliu would you mind taking a look if duplicate claims can break serverless scenarios?

@zackliu
Copy link
Member

zackliu commented Nov 13, 2020

@Chappitono It's a bug in SignalR Trigger. And the solution is still in investigation.
I've created an issue to track this bug.
#Azure/azure-functions-signalrservice-extension#148

@jwittner
Copy link

jwittner commented Dec 4, 2020

Just wanted to post that this emulator is a life saver for local development @vicancy . I hope it finds it's way soon onto the official docs. =)

@JagKing
Copy link

JagKing commented Dec 15, 2020

As above, the emulator has been a life saver, so big thanks @vicancy.

That said, when trying to hit the endpoints for to check / delete a connection id (/api/v1/hubs/{hub}/connections/{connectionId}) it will throw a NullReferenceException. From having a quick look, this is because in DynamicHubContextStore, on line 65 it is trying to get CachedHubLifetimeManager<> from the service provider but it is only registered as the implementation for HubLifetimeManager<>.

@Prabumca12
Copy link

Prabumca12 commented Dec 15, 2020

@vicancy, The emulator are working fine in local environment but it's not working once deployed function app and configured upstream settings in Azure portal. I can do only negotiate apart from no other response . Any reason?

@vicancy
Copy link
Member

vicancy commented Dec 16, 2020

@Prabumca12 does the client see any exception message when invoking methods?

@JagKing Thanks for reporting the issue, however I failed to repro it locally, could you share the repro step?

@Prabumca12
Copy link

Yes @vicancy. I'm getting below issue in client side

image

@JagKing
Copy link

JagKing commented Dec 16, 2020

@JagKing Thanks for reporting the issue, however I failed to repro it locally, could you share the repro step?

Sure.

Version: 1.0.0-preview1-10652

Steps:

  1. Install the emulator with dotnet tool install -g Microsoft.Azure.SignalR.Emulator --version 1.0.0-preview1-10652 --add-source https://www.myget.org/F/azure-signalr-dev/api/v3/index.json (I have also cloned it from the azure-signalr repo dev branch and run the project with the same results).
  2. Run the emulator with asrs-emulator start.
  3. In the local.settings.json of the azure function set AzureSignalRConnectionString to Endpoint=http://localhost;Port=8888;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGH;Version=1.0; (it has always been the default for me but change as required).
  4. Start the function app (I'm using C#).
  5. Load the front end and navigate to whatever should connect to signalr.
  6. After successful connection using Postman, attempt to send a GET/DELETE request to http://localhost:8888/api/v1/hubs/{hub}/connections/{connectionId}. It doesn't make a difference if {connectionId} is the actual connection id or a made up one, it will 500 either way but the {hub} must be the actual hub the front end connected to. For the Bearer Token, I have reused the one that the connection established in the step previous is using and made one specifically for the request with no difference.
  7. The following error is shown in the window running asrs-emulator:
    image
  8. Postman should show a 500 response, with an empty body.

A few other things:

  • I have reproduced this on two projects (that work fine with the hosted Azure SignalR Hub), one of which is the example chat project. Also I have reproduced it on my desktop and an azure vm.
  • If you attempt to run http://localhost:8888/api/v1/hubs/{hub}/connections/{connectionId} before any connections have been made (obviously the connection id is made up at this point) then no error is thrown and 404 is returned.

@Prabumca12
Copy link

Prabumca12 commented Dec 16, 2020

Yes @vicancy. I'm getting below issue in client side

image

@vicancy , Shall I raise a new issue for above one

@vicancy
Copy link
Member

vicancy commented Dec 16, 2020

[Updated typo]

@JagKing please try 1.0.0-preview1-10692

@Prabumca12
Copy link

@Prabumca12 Yep, you can raise a new issue for this. And can you provide a relatively simple client/hub codes that can reproduce this problem as I've tried some scenarios and can't repro the same issue.

@vicancy , Yeah sure . I will share code with you before I would like also know below upstream settings configured in azure portal are correct format or not? let me know I missed anything

image

@JagKing
Copy link

JagKing commented Dec 16, 2020

@vicancy Did you mean 1.0.0.preview1-10692? There wasn't a 1.6.2-preview1-10692 but there was the 1.0.0. I've tired the 1.0.0-preview1-10692 and it seems to be all good now, thanks.

@zackliu
Copy link
Member

zackliu commented Dec 16, 2020

@Prabumca12 Are you using azure function with SignalR Trigger, you should configure the url like
image

We have a step by step sample for upstream https://github.com/aspnet/AzureSignalR-samples/tree/master/samples/BidirectionChat
Please let me know if you have any questions.

@Prabumca12
Copy link

@zackliu. Yes . I have tried below configuration but it's not working

image

@ADH-LukeBollam
Copy link

ADH-LukeBollam commented Dec 17, 2020

No only serverless mode. For Default mode, please switch to use self-host SignalR, e.g. for netcore31, you can disable Azure SignalR through config:

{
  "Azure": {
    "SignalR": {
      "Enabled": false
    }
  }
}

@vicancy a couple of questions,

  1. Does this override AddAzureSignalR()? I'm getting an error "Microsoft.Azure.SignalR.Common.AzureSignalRConfigurationNoEndpointException: 'No connection string was specified.'". I thought if it was self-hosted, it wouldn't require a connection string.
  2. Can this be set to True instead of AddAzureSignalR()?

Also, do you have a reference available for the environment options available for this? I've tried to find documentation but cannot locate any.

@vicancy
Copy link
Member

vicancy commented Dec 18, 2020

@LukeBolly no it does not override AddAzureSignalR(). Basically if it is true it adds AddAzureSignalR() for you.

if (!context.HostingEnvironment.IsDevelopment() || context.Configuration.GetSection(Constants.Keys.AzureSignalREnabledKey).Get<bool>())
{
services.AddSignalR().AddAzureSignalR();

So please remove your AddAzureSignalR() if you are using this configuration. But it is really a good catch, creating an issue to track it: #1151

do you have a reference available for the environment options available for this

No not for now, we should add, thanks for pointing this out.

@mmulhearn
Copy link
Contributor

I'm running into an issue where I'm trying to send a message through the emulator and Newtonsoft is failing to parse the message. The upstream works and the function app's function is triggered. However, the function app throws

[2021-01-19T20:46:45.635Z] System.Private.CoreLib: Exception while executing function: agentchatrequest_getUpdates. Newtonsoft.Json: Additional text encountered after finished reading JSON content: ▲. Path '', line 1, position 1031.

Looking at the web socket messages, the message to the SignalR emulator over web socket ends with a unicode char:

[...]"invocationId":"0","target":"GetUpdates","type":1}�

The unicode char is \u1e or 9633. Seems Newtonsoft is dying on that. No one else is experiencing this? I know Default uses this as a message divider but it seems the serverless isn't properly handling that? Is it possible to turn it off? I don't see anything in the doco about that.

I'm using an HttpTrigger with an object parameter type.

public Task GetUpdates(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "hubs/" + ThisHubName + "/getUpdates")] object message,
            [SignalR(HubName = ThisHubName)] IAsyncCollector<SignalRMessage> signalRMessages)

@vicancy
Copy link
Member

vicancy commented Jan 20, 2021

@mmulhearn \u1e is the split character for SignalR json payloads and should be handled by the emulator. what's the message sending from the emulator to the function?

@mmulhearn
Copy link
Contributor

@vicancy This is the message being sent to the function (with my data removed)

"{\"type\":1,\"invocationId\":\"0\",\"target\":\"GetUpdates\",\"arguments\":[{\"removed\":\"removed\",\"removed\":\"removed\",\"removed\":\"removed\",\"removed\":\"removed\"}]}\u001e"

@mmulhearn
Copy link
Contributor

FYI, I also cloned the sigr repo and ran the emulator there with the same outcome.

@mmulhearn
Copy link
Contributor

Played around with it a bit more and it seems to only happen if your HttpTrigger is bound to object type. It works fine against HttpRequest, string, SignalRMessage, etc. The request still contains the \u1e but the deserialization into those types seem to handle it.

@mattwiz
Copy link

mattwiz commented Feb 2, 2021

@vicancy, when will there a be a public nuget package available?

@vicancy
Copy link
Member

vicancy commented Feb 3, 2021

The latest preview version is now available in NuGet. https://www.nuget.org/packages/Microsoft.Azure.SignalR.Emulator

@alexeymarkov
Copy link

If you do not need the Upstream functionality there is a simple emulator project I created a year ago:
https://github.com/alexeymarkov/AzureSignalRServiceSimulator

@alexeymarkov
Copy link

No problems since I run my own emulator 😂

@npalmiusfutr
Copy link

I am also noticing the issue that @mmulhearn describes above - I have raised #1262 for this.

@oatsoda
Copy link

oatsoda commented Apr 8, 2022

@vicancy Do you know if the emulator works with the Management SDK and the Persistent (websocket) Transport?

@oatsoda
Copy link

oatsoda commented Apr 8, 2022

@vicancy Do you know if the emulator works with the Management SDK and the Persistent (websocket) Transport?

Don't worry, I found my answer: https://github.com/Azure/azure-signalr/blob/dev/docs/emulator.md#azure-signalr-local-emulator

"Please also note that emulator only works for Transient transport type (the default one) and doesn't work for Persistent transport type."

Is this something that's likely to be added?

@vicancy
Copy link
Member

vicancy commented Apr 11, 2022

Is this something that's likely to be added?

There is currently no plan to. The emulator is designed to make local debugging easier. Since the development experience for using Transient mode and Persistent mode should be the same, the emulator sounds already achieved its goal by supporting Transient mdoe.

@JPL-Alliant
Copy link

My ServerlessHub isn't capturing the OnConnected Events but clients have no issues getting a connectionId via Negotiate function. I read a post to use Azure SignalR Emulator. when I do, I still don't get OnConnected event handler getting call and I noticed this error noted below but no further details are provided and there doesn't seem to be a verbose logging option. Any suggestions?

warn: Microsoft.Azure.SignalR.Emulator.HttpUpstreamTrigger[0]
      Failed to write message during operation {hub}=negotiatesignalr,{event}=connected,{category}=connections: An error occurred while sending the request.

FYI, this is the event I am trying to handle (even removing this from my ServerlessHub class I still get the noted error/warning):

        [FunctionName(nameof(OnConnected))]
        public async Task OnConnected([SignalRTrigger] InvocationContext invocationContext, ILogger logger)
        {
            logger.LogInformation($"{invocationContext.ConnectionId} has connected");
            await Task.CompletedTask;
        }

@vicancy
Copy link
Member

vicancy commented Apr 3, 2024

My ServerlessHub isn't capturing the OnConnected Events but clients have no issues getting a connectionId via Negotiate function. I read a post to use Azure SignalR Emulator. when I do, I still don't get OnConnected event handler getting call and I noticed this error noted below but no further details are provided and there doesn't seem to be a verbose logging option. Any suggestions?

warn: Microsoft.Azure.SignalR.Emulator.HttpUpstreamTrigger[0]
      Failed to write message during operation {hub}=negotiatesignalr,{event}=connected,{category}=connections: An error occurred while sending the request.

FYI, this is the event I am trying to handle (even removing this from my ServerlessHub class I still get the noted error/warning):

        [FunctionName(nameof(OnConnected))]
        public async Task OnConnected([SignalRTrigger] InvocationContext invocationContext, ILogger logger)
        {
            logger.LogInformation($"{invocationContext.ConnectionId} has connected");
            await Task.CompletedTask;
        }

Have you configured the upstream settings? The hub name is negotiatesignalr, does it match the config in your Function? You could also provide a minimum repro-able request for me to take a look.

@JPL-Alliant
Copy link

JPL-Alliant commented Apr 3, 2024

My ServerlessHub isn't capturing the OnConnected Events but clients have no issues getting a connectionId via Negotiate function. I read a post to use Azure SignalR Emulator. when I do, I still don't get OnConnected event handler getting call and I noticed this error noted below but no further details are provided and there doesn't seem to be a verbose logging option. Any suggestions?

warn: Microsoft.Azure.SignalR.Emulator.HttpUpstreamTrigger[0]
      Failed to write message during operation {hub}=negotiatesignalr,{event}=connected,{category}=connections: An error occurred while sending the request.

FYI, this is the event I am trying to handle (even removing this from my ServerlessHub class I still get the noted error/warning):

        [FunctionName(nameof(OnConnected))]
        public async Task OnConnected([SignalRTrigger] InvocationContext invocationContext, ILogger logger)
        {
            logger.LogInformation($"{invocationContext.ConnectionId} has connected");
            await Task.CompletedTask;
        }

Have you configured the upstream settings? The hub name is negotiatesignalr, does it match the config in your Function? You could also provide a minimum repro-able request for me to take a look.

Yes, I have configured the upstream settings and yes it matches, "negotiatesignalr". Like I mentioned, my clients are able to connect through the Negotiate function (which reaches out to Azure to get access_token) and the client can get a connectionId. All the HTTP communication works, but none of the non-HttpTrigger functions seem to work (OnConnected or my custom message functions) and there is an issue with being able to get the OnConnected event to get to the handler function as there is an error getting thrown as noted in my previous post. So you being able to repro on your end may not be doable and I am more looking into having Azure SignalR Emulator to provide more details as to why the noted error occurred as having, "An error occurred while sending the request." is a good start but no nearly enough details to look into this issue. Even without Azure SignalR Emulator running the OnConnected event doesn't get triggered, here is my Negotiate function, which works fine:

  [FunctionName("Negotiate")]
        public SignalRConnectionInfo Negotiate(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
            [SignalRConnectionInfo(HubName = "NegotiateSignalR")] SignalRConnectionInfo connectionInfo)
        {
            return connectionInfo;
        }

@JPL-Alliant
Copy link

JPL-Alliant commented Apr 3, 2024

Further testing, it seems transport was not set and I needed to set this setting, "transport: HttpTransportType.WebSockets":

this.hubConnection = new HubConnectionBuilder()
.withUrl('https://localhost:7071/api', {
transport: HttpTransportType.WebSockets
})
.build();

And when I do, I get this error and not sure if this created a new issue or just allowed me to view the actual error that is getting thrown for my SignalRTrigger function?:

Error invoking MyCustomSignalRTriggerFunction method: Error: Invocation failed, status code 500
at _this.callbacks. (HubConnection.js:410:36)
at HubConnection.processIncomingData (HubConnection.js:522:29)
at HubConnection.connection.onreceive (HubConnection.js:74:68)
at webSocket.onmessage [as __zone_symbol__ON_PROPERTYmessage] (WebSocketTransport.js:126:47)
at WebSocket.wrapFn (zone.js:815:43)
at _ZoneDelegate.invokeTask (zone.js:443:35)
at Object.onInvokeTask (core.mjs:26237:33)
at _ZoneDelegate.invokeTask (zone.js:442:64)
at Zone.runTask (zone.js:214:51)
at ZoneTask.invokeTask [as invoke] (zone.js:525:38)

And with or without Azure SignalR Emulator, I see this getting sent from the Network tab (HTTP Status Code 101 - Changing Protocol) to:

wss://my-demo.service.signalr.net/client/?hub=negotiatesignalr&id=[some-id]&access_token=[my-token]

with the following Messages over this wss socket connection (seems this may be getting blocked on my local machine?):
{"protocol":"json","version":1}
{"arguments":[{}],"invocationId":"0","target":"GetConnectionId","type":1}
{"type":6} <== repeated as time goes

@vicancy
Copy link
Member

vicancy commented Apr 7, 2024

Hi @JPL-Alliant , is what described here #1926 work for you? I am updating the emulator to print the full exception, before the package release, one way to debug your issue might be local running the emulator project to see what throws https://github.com/Azure/azure-signalr/tree/dev/src/Microsoft.Azure.SignalR.Emulator

@JPL-Alliant
Copy link

Hello @vicancy, yes what was noted in #1926 addressed my issue. A couple of things to be aware of:

  1. If you are testing/developing locally and using an Azure endpoint (example: xxxx.service.signalr.net) then your local OnConnected or Disconnected handlers will not get called but you will be able to handle and debug the 'negotiate' handler

  2. Using Azure SignalR Emulator locally addresses 1)

  3. Having and knowing the method names (not the Azure Function "FunctionName" names ) should be, "OnConnected" and "OnDisconnected" was key

  4. CORS setting is a bit tricky and developer should really take note of this when detailed in MS' Azure example

also, having more debug information would be great in Azure SignalR Emulator, but while running that in debug mode locally and getting the error while debugging doesn't seem to yield any more details than the HttpClient Send error of, "An error occurred while sending the request." Would be great if your finds provides more debug details and would look forward to testing the latest version of Azure SignalR Emulator to determine what the actual error is, my guess is Azure SignalR connection cannot connect to my local running instance for the OnConnect/OnDisconnected handlers.

Please keep me posted on the status of the latest build for ASE. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.