From 6ef52fc2b0103c0903ae24b75330af4f8c40fce6 Mon Sep 17 00:00:00 2001 From: Elena Kolevska Date: Mon, 13 Jan 2025 16:29:54 +0000 Subject: [PATCH] sync from upstream (#7) Signed-off-by: Elena Kolevska Signed-off-by: Elena Kolevska --- CHANGELOG.md | 3 ++- README.md | 2 +- durabletask/internal/shared.py | 17 ++++++++++++ tests/test_client.py | 49 +++++++++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a09078d..286312c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changes -- Protos are compiled with gRPC 1.62.3 / protobuf 3.25.X instead of the latest release. This ensures compatibility with a wider range of grpcio versions for better compatibility with other packages / libraries. +- Protos are compiled with gRPC 1.62.3 / protobuf 3.25.X instead of the latest release. This ensures compatibility with a wider range of grpcio versions for better compatibility with other packages / libraries ([#36](https://github.com/microsoft/durabletask-python/pull/36)) - by [@berndverst](https://github.com/berndverst) +- Http and grpc protocols and their secure variants are stripped from the host name parameter if provided. Secure mode is enabled if the protocol provided is https or grpcs ([#38](https://github.com/microsoft/durabletask-python/pull/38) - by [@berndverst)(https://github.com/berndverst) ### Updates diff --git a/README.md b/README.md index 443ea99..94be7ec 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ Orchestrations can specify retry policies for activities and sub-orchestrations. ### Prerequisites -- Python 3.8 +- Python 3.9 - A Durable Task-compatible sidecar, like [Dapr Workflow](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/) ### Installing the Durable Task Python client SDK diff --git a/durabletask/internal/shared.py b/durabletask/internal/shared.py index 400529a..c4f3aa4 100644 --- a/durabletask/internal/shared.py +++ b/durabletask/internal/shared.py @@ -15,6 +15,9 @@ # and should be deserialized as a SimpleNamespace AUTO_SERIALIZED = "__durabletask_autoobject__" +SECURE_PROTOCOLS = ["https://", "grpcs://"] +INSECURE_PROTOCOLS = ["http://", "grpc://"] + def get_default_host_address() -> str: return "localhost:4001" @@ -27,6 +30,20 @@ def get_grpc_channel( if host_address is None: host_address = get_default_host_address() + for protocol in SECURE_PROTOCOLS: + if host_address.lower().startswith(protocol): + secure_channel = True + # remove the protocol from the host name + host_address = host_address[len(protocol):] + break + + for protocol in INSECURE_PROTOCOLS: + if host_address.lower().startswith(protocol): + secure_channel = False + # remove the protocol from the host name + host_address = host_address[len(protocol):] + break + if secure_channel: channel = grpc.secure_channel(host_address, grpc.ssl_channel_credentials()) else: diff --git a/tests/test_client.py b/tests/test_client.py index b27f8e3..caacf65 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,4 +1,4 @@ -from unittest.mock import patch +from unittest.mock import patch, ANY from durabletask.internal.shared import (DefaultClientInterceptorImpl, get_default_host_address, @@ -39,3 +39,50 @@ def test_get_grpc_channel_with_metadata(): assert args[0] == mock_channel.return_value assert isinstance(args[1], DefaultClientInterceptorImpl) assert args[1]._metadata == METADATA + + +def test_grpc_channel_with_host_name_protocol_stripping(): + with patch('grpc.insecure_channel') as mock_insecure_channel, patch( + 'grpc.secure_channel') as mock_secure_channel: + + host_name = "myserver.com:1234" + + prefix = "grpc://" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "http://" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "HTTP://" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "GRPC://" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "" + get_grpc_channel(prefix + host_name, METADATA) + mock_insecure_channel.assert_called_with(host_name) + + prefix = "grpcs://" + get_grpc_channel(prefix + host_name, METADATA) + mock_secure_channel.assert_called_with(host_name, ANY) + + prefix = "https://" + get_grpc_channel(prefix + host_name, METADATA) + mock_secure_channel.assert_called_with(host_name, ANY) + + prefix = "HTTPS://" + get_grpc_channel(prefix + host_name, METADATA) + mock_secure_channel.assert_called_with(host_name, ANY) + + prefix = "GRPCS://" + get_grpc_channel(prefix + host_name, METADATA) + mock_secure_channel.assert_called_with(host_name, ANY) + + prefix = "" + get_grpc_channel(prefix + host_name, METADATA, True) + mock_secure_channel.assert_called_with(host_name, ANY) \ No newline at end of file