├── .github
└── workflows
│ ├── osx.yml
│ └── ubuntu.yml
├── README.md
├── SECURITY.md
├── docs
└── sharding.md
└── images
└── icon.png
/.github/workflows/osx.yml:
--------------------------------------------------------------------------------
1 | name: Gated-OSX
2 |
3 | on:
4 | push:
5 | branches:
6 | - dev
7 | - master
8 | pull_request:
9 | branches:
10 | - dev
11 | - master
12 |
13 | jobs:
14 | build_osx:
15 |
16 | runs-on: macos-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | - name: Setup .NET 2.1
21 | uses: actions/setup-dotnet@v1
22 | with:
23 | dotnet-version: '2.1.x'
24 | - name: Setup .NET 3.1
25 | uses: actions/setup-dotnet@v1
26 | with:
27 | dotnet-version: '3.1.x'
28 | - name: Setup .NET 5.0
29 | uses: actions/setup-dotnet@v1
30 | with:
31 | dotnet-version: '5.0.x'
32 | - name: Restore dependencies
33 | run: dotnet restore
34 | - name: Install packages
35 | run: brew install maven
36 | - name: Build
37 | run: |
38 | ./build.sh --ci
39 | mvn clean package -f ./binding-library/java/pom.xml
40 |
--------------------------------------------------------------------------------
/.github/workflows/ubuntu.yml:
--------------------------------------------------------------------------------
1 | name: Gated-Ubuntu
2 |
3 | on:
4 | push:
5 | branches:
6 | - dev
7 | - master
8 | pull_request:
9 | branches:
10 | - dev
11 | - master
12 |
13 | jobs:
14 | build_ubuntu:
15 |
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | - name: Setup .NET 2.1
21 | uses: actions/setup-dotnet@v1
22 | with:
23 | dotnet-version: '2.1.x'
24 | - name: Setup .NET 3.1
25 | uses: actions/setup-dotnet@v1
26 | with:
27 | dotnet-version: '3.1.x'
28 | - name: Setup .NET 5.0
29 | uses: actions/setup-dotnet@v1
30 | with:
31 | dotnet-version: '5.0.x'
32 | - name: Restore dependencies
33 | run: dotnet restore
34 | - name: Install packages
35 | run: sudo apt -y install libunwind8 maven
36 | - name: Build
37 | run: |
38 | ./build.sh --ci
39 | mvn clean package -f ./binding-library/java/pom.xml
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Azure Functions Bindings for Azure SignalR Service
2 | **Project moved to [azure-sdk-for-net](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService) repo.**
3 |
4 | The code in `dev` branch is moved to [`archived-dev`](https://github.com/Azure/azure-functions-signalrservice-extension/tree/archived-dev) branch.
5 |
6 | ## Build Status
7 |
8 | Travis: [](https://travis-ci.org/Azure/azure-functions-signalrservice-extension)
9 |
10 | ## NuGet Packages
11 |
12 | Package Name | Target Framework | NuGet
13 | ---|---|---
14 | Microsoft.Azure.WebJobs.Extensions.SignalRService | .NET Core App 2.1
.NET Core App 3.1 | [](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.SignalRService)
15 |
16 |
17 | ## Intro
18 |
19 | These bindings allow Azure Functions to integrate with [Azure SignalR Service](http://aka.ms/signalr_service).
20 |
21 | ### Supported scenarios
22 |
23 | - Allow clients to serverlessly connect to a SignalR Service hub without requiring an ASP.NET Core backend
24 | - Use Azure Functions (any language supported by V2) to broadcast messages to all clients connected to a SignalR Service hub.
25 | - Use Azure Functions (any language supported by V2) to send messages to a single user, or all the users in a group.
26 | - Use Azure Functions (any language supported by V2) to manage group users like add/remove a single user in a group.
27 | - Example scenarios include: broadcast messages to a SignalR Service hub on HTTP requests and events from Cosmos DB change feed, Event Hub, Event Grid, etc
28 | - Use multiple Azure SignalR Service instances for resiliency and disaster recovery in Azure Functions. See details in [Multiple SignalR service endpoint support](./docs/sharding.md).
29 |
30 | ### Bindings
31 |
32 | `SignalRConnectionInfo` input binding makes it easy to generate the token required for clients to initiate a connection to Azure SignalR Service.
33 |
34 | `SignalR` output binding allows messages to be broadcast to an Azure SignalR Service hub.
35 |
36 | ## Prerequisites
37 |
38 | - [Azure Functions Core Tools](https://github.com/Azure/azure-functions-core-tools) (V2 or V3)
39 |
40 | ## Usage
41 |
42 | ### Create Azure SignalR Service instance
43 |
44 | 1. Create Azure SignalR Service instances in the Azure Portal. Note the connection strings, you'll need them later.
45 |
46 | ### Create Function App with extension
47 |
48 | 1. In a new folder, create a new Azure Functions app.
49 | - `func init`
50 | 1. Install this Functions extension.
51 | - `func extensions install -p Microsoft.Azure.WebJobs.Extensions.SignalRService -v 1.0.0`
52 |
53 | ### Add application setting for SignalR connection string
54 |
55 | 1. Create an app setting called `AzureSignalRConnectionString` with the SignalR connection string.
56 | - On localhost, use `local.settings.json`
57 | - In Azure, use App Settings
58 |
59 | ### Using the SignalRConnectionInfo input binding
60 |
61 | In order for a client to connect to SignalR, it needs to obtain the SignalR Service client hub URL and an access token.
62 |
63 | 1. Create a new function named `negotiate` and use the `SignalRConnectionInfo` input binding to obtain the connection information and return it. Take a look at this [sample](samples/simple-chat/js/functionapp/negotiate/).
64 | 1. Client connects to the `negotiate` function as it's a normal SignalR hub. See [this file](samples/simple-chat/content/index.html) for a sample usage.
65 |
66 | Binding schema:
67 |
68 | ```javascript
69 | {
70 | "type": "signalRConnectionInfo",
71 | "name": "connectionInfo",
72 | "hubName": "",
73 | "connectionStringSetting": "", // Defaults to AzureSignalRConnectionString
74 | "direction": "in"
75 | }
76 | ```
77 |
78 | ### Using the SignalR output binding
79 |
80 | The `SignalR` output binding can be used to broadcast messages to all clients connected a hub. Take a look at this sample:
81 |
82 | - [HttpTrigger function to send messages](samples/simple-chat/js/functionapp/messages/)
83 | - [Simple chat app](samples/simple-chat/content/index.html)
84 | - Calls negotiate endpoint to fetch connection information
85 | - Connects to SignalR Service
86 | - Sends messages to HttpTrigger function, which then broadcasts the messages to all clients
87 |
88 | Binding schema:
89 |
90 | ```javascript
91 | {
92 | "type": "signalR",
93 | "name": "signalRMessages", // name of the output binding
94 | "hubName": "",
95 | "connectionStringSetting": "", // Defaults to AzureSignalRConnectionString
96 | "direction": "out"
97 | }
98 | ```
99 |
100 | To send one or more messages, set the output binding to an array of objects:
101 |
102 | ```javascript
103 | module.exports = function (context, req) {
104 | context.bindings.signalRMessages = [{
105 | "target": "newMessage", // name of the client method to invoke
106 | "arguments": [
107 | req.body // arguments to pass to client method
108 | ]
109 | }];
110 | context.done();
111 | };
112 | ```
113 |
114 | ## Contributing
115 |
116 | This project welcomes contributions and suggestions. Most contributions require you to agree to a
117 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
118 | the rights to use your contribution. For details, visit https://cla.microsoft.com.
119 |
120 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
121 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
122 | provided by the bot. You will only need to do this once across all repos using our CLA.
123 |
124 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
125 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
126 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
127 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docs/sharding.md:
--------------------------------------------------------------------------------
1 | # Multiple Azure SignalR Service Instances Support in Azure Functions
2 | Currently we add support for configuring multiple SignalR Service instances. You can distribute your clients to multiple SignalR service instances and send messages to multiple instances as if to one instance.
3 |
4 |
5 |
6 | - [Multiple Azure SignalR Service Instances Support in Azure Functions](#multiple-azure-signalr-service-instances-support-in-azure-functions)
7 | - [Usage scenarios](#usage-scenarios)
8 | - [Limitations](#limitations)
9 | - [Configuration](#configuration)
10 | - [Routing](#routing)
11 | - [Default behavior](#default-behavior)
12 | - [Customization](#customization)
13 | - [CSharp](#csharp)
14 | - [Other languages](#other-languages)
15 | - [Client routing](#client-routing)
16 | - [Messages routing](#messages-routing)
17 |
18 |
19 |
20 | ## Usage scenarios
21 | Routing logic is the way to decide to which SignalR Service instance among multiple instances your clients connect and your messages send. By applying different routing logic, this feature can be used in different scenarios.
22 | * Scaling. Randomly route each client to one SignalR Service instance, send messages to all the SignalR Service instances so that you can scale the concurrent connections.
23 | * Cross-geo scenario. Cross-geo networks can be comparatively unstable. Route your clients to a SignalR Service instance in the same region can reduce cross-geo connections.
24 | * High availability and disaster recovery scenarios. Set up multiple service instances in different regions, so when one region is down, the others can be used as backup. Configure service instances as two roles, **primary** and **secondary**. By default, clients will be routed to a primary online instance. When SDK detects all the primary instances are down, it will route clients to secondary instances. Clients connected before will experience connection drops when there is a disaster and failover take place. You'll need to handle such cases at client side to make it transparent to your end customers. For example, do reconnect after a connection is closed.
25 |
26 | ## Limitations
27 | Currently multiple-endpoint feature is only supported on `Persistent` transport type.
28 |
29 | ## Configuration
30 |
31 | To enable multiple SignalR Service instances, you should:
32 |
33 | 1. Use `Persistent` transport type.
34 |
35 | The default transport type is `Transient` mode. You should add the following entry to your `local.settings.json` file or the application setting on Azure.
36 |
37 | ```json
38 | {
39 | "AzureSignalRServiceTransportType":"Persistent"
40 | }
41 | ```
42 | >Notes for switching from `Transient` mode to `Persistent` mode on **Azure Functions runtime V3** :
43 | >
44 | > Under `Transient` mode, `Newtonsoft.Json` library is used to serialize arguments of hub methods, however, under `Persistent` mode, `System.Text.Json` library is used as default on Azure Functions runtime V3. `System.Text.Json` has some key differences in default behavior with `Newtonsoft.Json`. If you want to use `Newtonsoft.Json` under `Persistent` mode, you can add a configuration item: `"Azure:SignalR:HubProtocol":"NewtonsoftJson"` in `local.settings.json` file or `Azure__SignalR__HubProtocol=NewtonsoftJson` on Azure portal.
45 |
46 |
47 | 2. Configure multiple SignalR Service endpoints entries in your configuration.
48 |
49 | We use a [`ServiceEndpoint`](https://github.com/Azure/azure-signalr/blob/dev/src/Microsoft.Azure.SignalR.Common/Endpoints/ServiceEndpoint.cs) object to represent a SignalR Service instance. You can define an service endpoint with its `` and `` in the entry key, and the connection string in the entry value. The keys are in the following format :
50 |
51 | ```
52 | Azure:SignalR:Endpoints::
53 | ```
54 |
55 | `` is optional and defaults to `primary`. See samples below:
56 |
57 | ```json
58 | {
59 | "Azure:SignalR:Endpoints:EastUs":"",
60 |
61 | "Azure:SignalR:Endpoints:EastUs2:Secondary":"",
62 |
63 | "Azure:SignalR:Endpoints:WestUs:Primary":""
64 | }
65 | ```
66 |
67 | > * When you configure Azure SignalR endpoints in the App Service on Azure portal, don't forget to replace `":"` with `"__"`, the double underscore in the keys. For reasons, see [Environment variables](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#environment-variables).
68 | >
69 | > * Connection string configured with the key `{ConnectionStringSetting}` (defaults to "AzureSignalRConnectionString") is also recognized as a primary service endpoint with empty name. But this configuration style is not recommended for multiple endpoints.
70 | ## Routing
71 |
72 | ### Default behavior
73 | By default, the SDK uses the [DefaultEndpointRouter](https://github.com/Azure/azure-signalr/blob/dev/src/Microsoft.Azure.SignalR/EndpointRouters/DefaultEndpointRouter.cs) to pick up endpoints.
74 |
75 | * Client routing: Randomly select one endpoint from **primary online** endpoints. If all the primary endpoints are offline, then randomly select one **secondary online** endpoint. If the selection fails again, then exception is thrown.
76 |
77 | * Server message routing: All service endpoints are returned.
78 |
79 | ### Customization
80 | #### CSharp
81 |
82 | Here are the steps:
83 | * Implement a customized router. You can leverage information provided from [`ServiceEndpoint`](https://github.com/Azure/azure-signalr/blob/dev/src/Microsoft.Azure.SignalR.Common/Endpoints/ServiceEndpoint.cs) to make routing decision. See guide here: [customize-route-algorithm](https://github.com/Azure/azure-signalr/blob/dev/docs/sharding.md#customize-route-algorithm). **Please note that Http trigger is required in the negotiation function when you need `HttpContext` in custom negotiation method.**
84 |
85 | * Register the router to DI container.
86 | ```cs
87 | using Microsoft.Azure.Functions.Extensions.DependencyInjection;
88 | using Microsoft.Azure.SignalR;
89 | using Microsoft.Extensions.DependencyInjection;
90 |
91 | [assembly: FunctionsStartup(typeof(SimpleChatV3.Startup))]
92 | namespace SimpleChatV3
93 | {
94 | public class Startup : FunctionsStartup
95 | {
96 | public override void Configure(IFunctionsHostBuilder builder)
97 | {
98 | builder.Services.AddSingleton();
99 | }
100 | }
101 | }
102 | ```
103 |
104 | #### Other languages
105 |
106 | For languages other than C#, we support specifying target endpoints in each request. You will use new binding types to get endpoint information.
107 |
108 | ##### Client routing
109 | The `SignalRConnectionInfo` binding selects one endpoint according the default routing rule. If you want to customize routing rule, you should use `SignalRNegotiation` binding instead of `SignalRConnectionInfo` binding.
110 |
111 | `SignalRNegotiation` binding configuration properties are the same as `SignalRConnectionInfo`. Here's a `function.json` file sample:
112 | ```json
113 | {
114 | "type": "signalRNegotiation",
115 | "name": "negotiationContext",
116 | "hubName": "",
117 | "direction": "in"
118 | }
119 | ```
120 |
121 | You could also add other binding data such as `userId`, `idToken` and `claimTypeList` just like `SignalRConnectionInfo`.
122 |
123 | The object you get from `SignalRNegotiation` binding is in the following format:
124 | ```json
125 | {
126 | "endpoints": [
127 | {
128 | "endpointType": "Primary",
129 | "name": "",
130 | "endpoint": "https://****.service.signalr.net",
131 | "online": true,
132 | "connectionInfo": {
133 | "url": "",
134 | "accessToken": ""
135 | }
136 | },
137 | {
138 | "...": "..."
139 | }
140 | ]
141 | }
142 | ```
143 |
144 | Here's a Javascript usage sample of `SignalRNegotiation` binding:
145 | ```js
146 | module.exports = function (context, req, negotiationContext) {
147 | var userId = req.query.userId;
148 | if (userId.startsWith("east-")) {
149 | //return the first endpoint whose name starts with "east-" and status is online.
150 | context.res.body = negotiationContext.endpoints.find(endpoint => endpoint.name.startsWith("east-") && endpoint.online).connectionInfo;
151 | }
152 | else {
153 | //return the first online endpoint
154 | context.res.body = negotiationContext.endpoints.filter(endpoint => endpoint.online)[0].connectionInfo;
155 | }
156 | }
157 | ```
158 |
159 | ##### Messages routing
160 | Messages or actions routing needs two binding types to cooperate. In general, firstly you need a new input binding type `SignalREndpoints` to get all the available endpoint information. Then you filter the endpoints and get an array containing all the endpoints that you want to send to. Lastly you specify the target endpoints in the `SignalR` output binding.
161 |
162 | Here's the `SignalREndpoints` binding configuration properties in `functions.json` file:
163 | ```json
164 | {
165 | "type": "signalREndpoints",
166 | "direction": "in",
167 | "name": "endpoints",
168 | "hubName": ""
169 | }
170 | ```
171 |
172 | The object you get from `SignalREndpoints` is an array of endpoints each of which is represented as a JSON object with the following schema:
173 |
174 | ```json
175 | {
176 | "endpointType": "",
177 | "name": "",
178 | "endpoint": "https://****.service.signalr.net",
179 | "online": true
180 | }
181 | ```
182 |
183 |
184 | After you get the target endpoint array, add an `endpoints` property to the output binding object. This is a Javascript example:
185 | ```js
186 | module.exports = function (context, req, endpoints) {
187 | var targetEndpoints = endpoints.filter(endpoint => endpoint.name.startsWith("east-"));
188 | context.bindings.signalRMessages = [{
189 | "target": "chat",
190 | "arguments": ["hello-world"],
191 | "endpoints": targetEndpoints,
192 | }];
193 | context.done();
194 | }
195 | ```
196 |
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/azure-functions-signalrservice-extension/3fcc65f19c57686688f5e064da49e847b07cd614/images/icon.png
--------------------------------------------------------------------------------