Azure / azure-signalr

Azure SignalR Service SDK for .NET
https://aka.ms/signalr-service
MIT License
425 stars 100 forks source link

Once the negotiate function is hit i'm still not able to connect to the instance of signalR #1799

Open Vikas252 opened 1 year ago

Vikas252 commented 1 year ago

Describe the bug

i'm using signalR instance (serverless mode) i have created the bindings for signalR through the serverless framework, once the negotiate route is been hit i get the response but further when i try to connect with the same url and access token received from the negotiate function it gives error of HubConnection failed to start successfully because of error 'Error: Failed to complete negotiation with the server: TypeError: Cannot read properties of undefined (reading 'secure')'.

To Reproduce

function.json:

{
  "disabled": false,
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "route": "negotiate",
      "authLevel": "function",
      "methods": ["POST"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "signalRConnectionInfo",
      "name": "connectionInfo",
      "hubName": "serverless",
      "connectionStringSetting": "AzureSignalRConnectionString",
      "direction": "in"
    }
  ],
  "entryPoint": "negotiate",
  "scriptFile": "../dist/server.js"
}

Azure function handler (Number 1) i.e server.ts:

module.exports.negotiate = async (context, req, connectionInfo) => {
  console.log(connectionInfo, 'Serverless-signalR');
  const connection = new HubConnectionBuilder()
    .withUrl(`${connectionInfo.url}`, {
      accessTokenFactory: () =>
        `${connectionInfo.accessToken}`,
      // skipNegotiation: true,
      transport: HttpTransportType.WebSockets
    }).withAutomaticReconnect().configureLogging(LogLevel.Debug)
    .build();
  connection.start();
  context.res.body = connectionInfo;
};

Azure function handler (Number 2) i.e server.ts:

module.exports.negotiate = async (context, req, connectionInfo) => {
  console.log(connectionInfo, 'Serverless-signalR');
  // const connection = new HubConnectionBuilder()
  //   .withUrl(`${connectionInfo.url}`, {
  //     accessTokenFactory: () =>
  //       `${connectionInfo.accessToken}`,
  //     // skipNegotiation: true,
  //     transport: HttpTransportType.WebSockets
  //   }).withAutomaticReconnect().configureLogging(LogLevel.Debug)
  //   .build();
  // connection.start();
  context.res.body = connectionInfo;

Serverless.yml specification:

negotiate:
    handler: dist/server.negotiate
    events:
      - http: true
        route: negotiate
        methods:
          - POST
    outputs:
      connectionInfo:
        type: signalRConnectionInfo
        name: connectionInfo
        hubName: serverless
        connectionStringSetting: AzureSignalRConnectionString
        direction: in

app.ts:

import express from 'express';
import cors from 'cors';
import helmet from 'helmet';

const app = express();
app.use(helmet({
  xFrameOptions: { action: 'deny' },
}));
app.set('port', process.env.PORT || 3000);
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cors());

async function main() {
  try {
    app.listen(app.get('port'), () => console.log(`listening on port ${app.get('port')}`));
} catch (err) {
    console.error(err);
  }
}
main();

Exceptions (if any)

If azure handler is Number 1:

Executing 'Functions.negotiate' (Reason='This function was programmatically called via the host APIs.', Id=*****-****-****-*****)
Debug: Starting HubConnection.
Debug: Starting connection with transfer format 'Text'.
Sending negotiation request: https:/<base_url>/client/negotiate?hub=serverless&negotiateVersion=1.
Warning: Error from HTTP request. TypeError: Cannot read properties of undefined (reading 'secure').
Error: Failed to complete negotiation with the server: TypeError: Cannot read properties of undefined (reading 'secure')

If azure handler is Number 2

Executing 'Functions.negotiate' (Reason='This function was programmatically called via the host APIs.', Id=6057257d-f7d1-423f-b6dd-15a2d2f0968f)
[2023-06-21T08:05:26.009Z] {
[2023-06-21T08:05:26.011Z]   url: 'https://<base_url>/client/?hub=serverless',
[2023-06-21T08:05:26.012Z]   accessToken: '<token>'
[2023-06-21T08:05:26.014Z] } Serverless-signalR
[2023-06-21T08:05:26.224Z] Executed 'Functions.negotiate' (Succeeded, Id=6057257d-f7d1-423f-b6dd-15a2d2f0968f, Duration=426ms)

Further technical details

"azure-functions-core-tools": "^4.0.5148", "@microsoft/signalr": "^7.0.7",

vicancy commented 1 year ago

Where do you expect to start the connection? In Number1 looks like you are trying to start the connection from inside the Negotiate function. Could you describe your scenario a little bit more?

Vikas252 commented 1 year ago

@vicancy Thank you for the response the scenario can be like The system is built on nodejs with express multiple express routes, consist of handlers for different routes on of them is the negotiate route through which the signalR connection end point URL and access Token is obtained

I need to access or open websocket mode for serverless on a particular page when the user clicks maybe a different route, to communicate with my angular frontend so to connect the signalR in my backend when i get the connection endpoint and access token i have a controller which handles the connection for signalR so there i create a new connection with the existing json i received from the negotiate function called.

When tried with the controller it says Error from HTTP request. TypeError: Cannot read properties of undefined (reading 'secure').

For the response of negotiate function i called it explicitly from postman for testing in the above scenario

vicancy commented 1 year ago

I did a quick try on creating a negotiate function and it is successfully connected locally using the latest core tool version 4.0.5198. Here is my steps:

  1. open a codespace from https://github.com/aspnet/AzureSignalR-samples
  2. In the codespace terminal install func npm i -g azure-functions-core-tools
  3. under folder samples/QuickStartServerless/javascript
    1. Rename local.settings.template.json to local.settings.json
    2. Update AzureSignalRConnectionString to your Azure SignalR connection string
    3. Remove other subfolders and only keep folder negotiate (to avoid other dependencies)
  4. In terminal run npm i --save @microsoft/signalr
  5. Update negotaite/index.js to

    const { HubConnectionBuilder, HttpTransportType, LogLevel } = require("@microsoft/signalr");
    module.exports = async function (context, req, connectionInfo) {
      const connection = new HubConnectionBuilder()
        .withUrl(`${connectionInfo.url}`, {
          accessTokenFactory: () => `${connectionInfo.accessToken}`,
          transport: HttpTransportType.WebSockets
        })
        .withAutomaticReconnect()
        .configureLogging(LogLevel.Debug)
        .build();
    
      console.log(connectionInfo);
      await connection.start();
    
      context.res.body = connectionInfo;
    };
  6. In terminal run func start
  7. Start another terminal run curl -X POST http://localhost:7071/api/negotiate
  8. The logs show Debug: HubConnection connected successfully. image