Skip to content

Commit aa1aa8f

Browse files
authored
Merge pull request #275551 from samurp/python-websockets
Python websockets Tutorial for Azure Relay
2 parents 7faf59a + 27d261c commit aa1aa8f

6 files changed

+335
-5
lines changed

articles/azure-relay/TOC.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
href: relay-hybrid-connections-http-requests-node-get-started.md
1919
- name: Send and receive messages - Java HTTP
2020
href: relay-hybrid-connections-java-get-started.md
21+
- name: Send and receive messages - Python WebSockets
22+
href: relay-hybrid-connections-python-get-started.md
2123
- name: Windows Communication Foundation (WCF)
2224
items:
2325
- name: Expose an on-premises WCF service to a web application in the cloud
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
author: clemensv
3+
ms.service: service-bus-relay
4+
ms.topic: include
5+
ms.date: 05/16/2024
6+
ms.author: samurp
7+
---
8+
9+
### Create a Python Script
10+
11+
If you disabled the "Requires Client Authorization" option when creating the Relay,
12+
you can send requests to the Hybrid Connections URL with any browser. For accessing
13+
protected endpoints, you need to create and pass a SAS Token, which is shown here.
14+
15+
Here's a simple Python script that demonstrates sending requests to
16+
a Hybrid Connections URL with SAS Tokens utilizing WebSockets.
17+
18+
### Dependencies
19+
20+
1. Install the following Python libraries using pip before running the client application
21+
22+
`asyncio`, `json`, `logging`, `websockets`
23+
24+
These libraries can be installed using the following command:
25+
26+
```bash
27+
pip install <package name>
28+
```
29+
2. Generate a `config.json` file to store your connection details
30+
31+
```json
32+
{
33+
"namespace": "HYBRID_CONNECTION_NAMESPACE",
34+
"path": "HYBRID_CONNECTION_ENTITY_NAME",
35+
"keyrule": "SHARED_ACCESS_KEY_NAME",
36+
"key": "SHARED_ACCESS_PRIMARY_KEY"
37+
}
38+
```
39+
Replace the placeholders in brackets with the values you obtained when you created the hybrid connection.
40+
41+
- `namespace` - The Relay namespace. Be sure to use the fully qualified namespace name; for example, `{namespace}.servicebus.windows.net`.
42+
- `path` - The name of the hybrid connection.
43+
- `keyrule` - Name of your Shared Access Policies key, which is `RootManageSharedAccessKey` by default.
44+
- `key` - The primary key of the namespace you saved earlier.
45+
46+
3. Generate a helper function file for helper functions
47+
48+
The following file is used as `relaylib.py` and have helper functions for WebSocket URL generation and SAS tokens
49+
50+
[!INCLUDE [relay-python-helper-functions](relay-python-helper-functions.md)]
51+
52+
### Write some code to send messages
53+
54+
1. Ensure your dependency `config.json` and `relaylib.py` are available in your path
55+
56+
57+
2. Here's what your `sender.py` file should look like:
58+
59+
```python
60+
import asyncio
61+
import json
62+
import logging
63+
import relaylib
64+
import websockets
65+
66+
async def run_application(message, config):
67+
service_namespace = config["namespace"]
68+
entity_path = config["path"]
69+
sas_key_name = config["keyrule"]
70+
sas_key = config["key"]
71+
service_namespace += ".servicebus.windows.net"
72+
73+
# Configure logging
74+
logging.basicConfig(level=logging.DEBUG) # Enable debug logging
75+
76+
token = relaylib.createSasToken(service_namespace, entity_path, sas_key_name, sas_key)
77+
logging.debug("Token: %s", token)
78+
wss_uri = relaylib.createListenUrl(service_namespace, entity_path, token)
79+
logging.debug("WssURI: %s", wss_uri)
80+
81+
try:
82+
async with websockets.connect(wss_uri) as websocket:
83+
logging.info("Sending message to Azure Relay WebSocket...")
84+
await websocket.send(json.dumps({"message": message}))
85+
logging.info("Message sent: %s", message)
86+
except Exception as e:
87+
logging.error("An error occurred: %s", str(e))
88+
89+
if __name__ == "__main__":
90+
# Load configuration from JSON file
91+
with open("config.json") as config_file:
92+
config = json.load(config_file)
93+
94+
asyncio.run(run_application("This is a message to Azure Relay Hybrid Connections!", config))
95+
```
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
author: clemensv
3+
ms.service: service-bus-relay
4+
ms.topic: include
5+
ms.date: 05/16/2024
6+
ms.author: samurp
7+
---
8+
9+
### Create a Python Script
10+
11+
If you disabled the "Requires Client Authorization" option when creating the Relay,
12+
you can send requests to the Hybrid Connections URL with any browser. For accessing
13+
protected endpoints, you need to create and pass a SAS Token, which is shown here.
14+
15+
Here's a simple Python script that demonstrates sending requests to
16+
a Hybrid Connections URL with SAS Tokens utilizing WebSockets.
17+
18+
### Dependencies
19+
20+
1. Install the following Python libraries using pip before running the server application
21+
22+
`asyncio`, `json`, `logging`, `websockets`
23+
24+
These libraries can be installed using the following command:
25+
26+
```bash
27+
pip install <package name>
28+
```
29+
2. Generate a `config.json` file to store your connection details
30+
31+
```json
32+
{
33+
"namespace": "HYBRID_CONNECTION_NAMESPACE",
34+
"path": "HYBRID_CONNECTION_ENTITY_NAME",
35+
"keyrule": "SHARED_ACCESS_KEY_NAME",
36+
"key": "SHARED_ACCESS_PRIMARY_KEY"
37+
}
38+
```
39+
Replace the placeholders in brackets with the values you obtained when you created the hybrid connection.
40+
41+
- `namespace` - The Relay namespace. Be sure to use the fully qualified namespace name; for example, `{namespace}.servicebus.windows.net`.
42+
- `path` - The name of the hybrid connection.
43+
- `keyrule` - Name of your Shared Access Policies key, which is `RootManageSharedAccessKey` by default.
44+
- `key` - The primary key of the namespace you saved earlier.
45+
46+
3. Generate a helper function file for helper functions
47+
48+
The following file is used as `relaylib.py` and have helper functions for WebSocket URL generation and SAS tokens
49+
50+
[!INCLUDE [relay-python-helper-functions](relay-python-helper-functions.md)]
51+
52+
### Write some code to send messages
53+
54+
1. Ensure your dependency `config.json` and `relaylib.py` are available in your path
55+
56+
57+
2. Here's what your `listener.py` file should look like:
58+
59+
```python
60+
import asyncio
61+
import json
62+
import logging
63+
import relaylib
64+
import websockets
65+
66+
async def run_application(config):
67+
serviceNamespace = config["namespace"]
68+
entityPath = config["path"]
69+
sasKeyName = config["keyrule"]
70+
sasKey = config["key"]
71+
serviceNamespace += ".servicebus.windows.net"
72+
# Configure logging
73+
logging.basicConfig(level=logging.INFO) # Enable DEBUG/INFO logging as appropriate
74+
75+
try:
76+
logging.debug("Generating SAS Token for: %s", serviceNamespace)
77+
token = relaylib.createSasToken(serviceNamespace, entityPath, sasKeyName, sasKey)
78+
logging.debug("Generating WebSocket URI")
79+
wssUri = relaylib.createListenUrl(serviceNamespace, entityPath, token)
80+
async with websockets.connect(wssUri) as websocket:
81+
logging.info("Listening for messages on Azure Relay WebSocket...")
82+
while True:
83+
message = await websocket.recv()
84+
logging.info("Received message: %s", message)
85+
except KeyboardInterrupt:
86+
logging.info("Exiting listener.")
87+
88+
if __name__ == "__main__":
89+
# Load configuration from JSON file
90+
with open("config.json") as config_file:
91+
config = json.load(config_file)
92+
93+
asyncio.run(run_application(config))
94+
```
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
author: clemensv
3+
ms.service: service-bus-relay
4+
ms.topic: include
5+
ms.date: 05/16/2024
6+
ms.author: samurp
7+
---
8+
9+
### Create a Python Script
10+
11+
This script provides helper functions for applications utilizing Azure Relay Hybrid Connections.
12+
These functions likely assist with tasks like generating SAS tokens and establishing WebSocket
13+
connections for secure communication.
14+
15+
### Dependencies
16+
17+
Install the following Python libraries using pip before generating the helper function script: `base64`, `hashlib`, `hmac`, `math`, `time`, `urllib`
18+
19+
These libraries can be installed using the following command:
20+
21+
```bash
22+
pip install <package name>
23+
```
24+
25+
### Write the helper function script
26+
27+
Here's what your `relaylib.py` file should look like:
28+
29+
```python
30+
import base64
31+
import hashlib
32+
import hmac
33+
import math
34+
import time
35+
import urllib
36+
37+
# Function which generates the HMAC-SHA256 of a given message
38+
def hmac_sha256(key, msg):
39+
hash_obj = hmac.new(key=key, msg=msg, digestmod=hashlib._hashlib.openssl_sha256)
40+
return hash_obj.digest()
41+
42+
# Function to create a WebSocket URL for listening for a server application
43+
def createListenUrl(serviceNamespace, entityPath, token = None):
44+
url = 'wss://' + serviceNamespace + '/$hc/' + entityPath + '?sb-hc-action=listen&sb-hc-id=123456'
45+
if token is not None:
46+
url = url + '&sb-hc-token=' + urllib.parse.quote(token)
47+
return url
48+
49+
# Function which creates the url for the client application
50+
def createSendUrl(serviceNamespace, entityPath, token = None):
51+
url = 'wss://' + serviceNamespace + '/$hc/' + entityPath + '?sb-hc-action=connect&sb-hc-id=123456'
52+
if token is not None:
53+
url = url + '&sb-hc-token=' + urllib.parse.quote(token)
54+
return url
55+
56+
# Function which creates the Service Bus SAS token.
57+
def createSasToken(serviceNamespace, entityPath, sasKeyName, sasKey):
58+
uri = "http://" + serviceNamespace + "/" + entityPath
59+
encodedResourceUri = urllib.parse.quote(uri, safe = '')
60+
61+
# Define the token validity period in seconds (48 hours in this case)
62+
tokenValidTimeInSeconds = 60 * 60 * 48
63+
unixSeconds = math.floor(time.time())
64+
expiryInSeconds = unixSeconds + tokenValidTimeInSeconds
65+
66+
# Create the plain signature string by combining the encoded URI and the expiry time
67+
plainSignature = encodedResourceUri + "\n" + str(expiryInSeconds)
68+
69+
# Encode the SAS key and the plain signature as bytes
70+
sasKeyBytes = sasKey.encode("utf-8")
71+
plainSignatureBytes = plainSignature.encode("utf-8")
72+
hashBytes = hmac_sha256(sasKeyBytes, plainSignatureBytes)
73+
base64HashValue = base64.b64encode(hashBytes)
74+
75+
# Construct the SAS token string
76+
token = "SharedAccessSignature sr=" + encodedResourceUri + "&sig=" + urllib.parse.quote(base64HashValue) + "&se=" + str(expiryInSeconds) + "&skn=" + sasKeyName
77+
return token
78+
```

articles/azure-relay/index.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ landingContent:
3030
linkLists:
3131
- linkListType: tutorial
3232
links:
33-
- text: Send and receive messages using Websockets - .NET
33+
- text: Send and receive messages using WebSockets - .NET
3434
url: relay-hybrid-connections-dotnet-get-started.md
3535
- text: Send and receive messages using HTTP - .NET
3636
url: relay-hybrid-connections-http-requests-dotnet-get-started.md
37-
- text: Send and receive messages using Websockets - Node.js
37+
- text: Send and receive messages using WebSockets - Node.js
3838
url: relay-hybrid-connections-node-get-started.md
3939
- text: Send and receive messages using HTTP - Node.js
4040
url: relay-hybrid-connections-http-requests-node-get-started.md
41+
- text: Send and receive messages using WebSockets - Python
42+
url: relay-hybrid-connections-python-get-started.md
4143
- linkListType: download
4244
links:
4345
- text: Samples on GitHub
@@ -48,11 +50,11 @@ landingContent:
4850
linkLists:
4951
- linkListType: tutorial
5052
links:
51-
- text: Expose an on-prem WCF service to a web application in the cloud
53+
- text: Expose an on-premises WCF service to a web application in the cloud
5254
url: service-bus-dotnet-hybrid-app-using-service-bus-relay.md
53-
- text: Expose an on-prem WCF service to a WCF service outside your network
55+
- text: Expose an on-premises WCF service to a WCF service outside your network
5456
url: service-bus-relay-tutorial.md
55-
- text: Expose an on-prem WCF REST service to a client outside your network
57+
- text: Expose an on-premises WCF REST service to a client outside your network
5658
url: service-bus-relay-rest-tutorial.md
5759
- linkListType: download
5860
links:
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
title: Azure Relay Hybrid Connections - WebSocket requests in Python
3+
description: Write a Python console application for Azure Relay Hybrid Connections WebSocket requests.
4+
ms.topic: tutorial
5+
ms.date: 05/16/2024
6+
ms.custom: devx-track-Python, mode-ui, mode-api, devx-track-extended-Python
7+
---
8+
9+
# Get started with Relay Hybrid Connections WebSocket requests in Python
10+
11+
[!INCLUDE [relay-selector-hybrid-connections](./includes/relay-selector-hybrid-connections.md)]
12+
13+
In this quickstart, you create Python sender and receiver applications that send and receive messages by using the WebSocket protocol. The applications use Hybrid Connections feature of Azure Relay. To learn about Azure Relay in general, see [Azure Relay](relay-what-is-it.md).
14+
15+
In this quickstart, you take the following steps:
16+
17+
1. Create a Relay namespace by using the Azure portal.
18+
1. Create a hybrid connection in that namespace by using the Azure portal.
19+
1. Generate a config.json properties file to store connection details
20+
1. Develop a relaylib.py for helper functions (SAS tokens, URLs)
21+
1. Write a server (listener) script to receive messages.
22+
1. Write a client (sender) script to send messages.
23+
1. Execute the server (listener) script and optionally the client (sender) script.
24+
25+
## Prerequisites
26+
- [Python](https://www.Python.com/en/). Ensure that you're running Python 3.10+
27+
- An Azure subscription. If you don't have one, [create a free account](https://azure.microsoft.com/free/) before you begin.
28+
29+
## Create a namespace using the Azure portal
30+
[!INCLUDE [relay-create-namespace-portal](./includes/relay-create-namespace-portal.md)]
31+
32+
## Create a hybrid connection using the Azure portal
33+
[!INCLUDE [relay-create-hybrid-connection-portal](./includes/relay-create-hybrid-connection-portal.md)]
34+
35+
## Develop helper functions
36+
[!INCLUDE [relay-python-helper-functions](./includes/relay-python-helper-functions.md)]
37+
38+
## Create a server application (listener)
39+
To listen and receive messages from the Relay, write a Python WebSocket Server script.
40+
41+
[!INCLUDE [relay-hybrid-connections-python-get-started-server](./includes/relay-hybrid-connections-websocket-requests-python-get-started-server.md)]
42+
43+
## Create a client application (sender)
44+
45+
To send messages to the Relay, you can use any HTTP or WebSocket client, the sample included is a python implementation.
46+
47+
[!INCLUDE [relay-hybrid-connections-python-get-started-client](./includes/relay-hybrid-connections-websocket-requests-python-get-started-client.md)]
48+
49+
## Run the applications
50+
51+
1. Run the server application: from a command prompt type `python3 listener.py`.
52+
2. Run the client application: from a command prompt type `python3 sender.py`.
53+
54+
Congratulations, you have created an end-to-end Hybrid Connections application using Python!
55+
56+
## Next steps
57+
In this quickstart, you created Python client and server applications that used WebSockets to send and receive messages. The Hybrid Connections feature of Azure Relay also supports using HTTP to send and receive messages. To learn how to use HTTP with Azure Relay Hybrid Connections, see the [HTTP quickstart](relay-hybrid-connections-node-get-started.md).
58+
59+
In this quickstart, you used Python to create client and server applications. To learn how to write client and server applications using .NET Framework, see the [.NET HTTP quickstart](relay-hybrid-connections-dotnet-get-started.md) or the [.NET HTTP quickstart](relay-hybrid-connections-http-requests-dotnet-get-started.md).

0 commit comments

Comments
 (0)